@@ -139,15 +139,25 @@ async function settle() {
139139 await 0
140140}
141141
142+ // group(RUN/COPY 共用コンテナ)経由でボタンを取り出すヘルパー
143+ function buttonGroupOf ( elem ) { return elem . childByClass ( 'highlight__button-group' ) }
144+ function copyButtonOf ( elem ) {
145+ const group = buttonGroupOf ( elem )
146+ return group && group . childByClass ( 'highlight__copy-button' )
147+ }
148+
142149// A ruby sample keeps getting the COPY button (regression)
143150{
144151 const spy = clipboardSpy ( )
145152 const pre = new FakeElement ( 'pre' , 'highlight ruby' )
146153 pre . appendChild ( new FakeElement ( 'code' , '' , 'puts 1\n' ) )
147154 runOnload ( [ pre ] , spy . navigator )
148- const btn = pre . childByClass ( 'highlight__copy-button' )
155+ const btn = copyButtonOf ( pre )
149156 assert ( btn !== null , 'pre.highlight.ruby gets a COPY button' )
150- assert ( pre . firstChild === btn , 'COPY button is prepended as the first child' )
157+ assert ( pre . firstChild === buttonGroupOf ( pre ) ,
158+ 'the shared button group is prepended as the first child' )
159+ assert ( buttonGroupOf ( pre ) . firstChild === btn ,
160+ 'COPY button lives inside the button group' )
151161 btn . onclick ( )
152162 await settle ( )
153163 assert ( spy . written . length === 1 && spy . written [ 0 ] === 'puts 1\n' ,
@@ -161,7 +171,7 @@ async function settle() {
161171 const spy = clipboardSpy ( )
162172 const pre = new FakeElement ( 'pre' , '' , 'ary = []\n' )
163173 runOnload ( [ pre ] , spy . navigator )
164- const btn = pre . childByClass ( 'highlight__copy-button' )
174+ const btn = copyButtonOf ( pre )
165175 assert ( btn !== null , 'plain <pre> without highlight class gets a COPY button' )
166176 btn . onclick ( )
167177 await settle ( )
@@ -174,7 +184,7 @@ async function settle() {
174184 const pre = new FakeElement ( 'pre' , 'highlight c' )
175185 pre . appendChild ( new FakeElement ( 'code' , '' , 'VALUE v;\n' ) )
176186 runOnload ( [ pre ] , clipboardSpy ( ) . navigator )
177- assert ( pre . childByClass ( 'highlight__copy-button' ) !== null ,
187+ assert ( copyButtonOf ( pre ) !== null ,
178188 'pre.highlight.c gets a COPY button' )
179189}
180190
@@ -183,7 +193,7 @@ async function settle() {
183193 const div = new FakeElement ( 'div' , 'highlight' )
184194 const pre = new FakeElement ( 'pre' , '' )
185195 runOnload ( [ div , pre ] , clipboardSpy ( ) . navigator )
186- assert ( div . childByClass ( 'highlight__copy-button' ) === null ,
196+ assert ( copyButtonOf ( div ) === null ,
187197 'non-pre element does not get a COPY button' )
188198}
189199
@@ -194,7 +204,7 @@ async function settle() {
194204 pre . appendChild ( new FakeElement ( 'span' , 'caption' , '例' ) )
195205 pre . appendChild ( new FakeElement ( 'code' , '' , 'p 42\n' ) )
196206 runOnload ( [ pre ] , spy . navigator )
197- pre . childByClass ( 'highlight__copy-button' ) . onclick ( )
207+ copyButtonOf ( pre ) . onclick ( )
198208 await settle ( )
199209 assert ( spy . written [ 0 ] === 'p 42\n' ,
200210 'caption text is excluded from the copied text' )
@@ -207,7 +217,7 @@ async function settle() {
207217 const spy = clipboardSpy ( )
208218 const pre = new FakeElement ( 'pre' , '' , '\n\nputs 1\n\n\n' )
209219 runOnload ( [ pre ] , spy . navigator )
210- pre . childByClass ( 'highlight__copy-button' ) . onclick ( )
220+ copyButtonOf ( pre ) . onclick ( )
211221 await settle ( )
212222 assert ( spy . written [ 0 ] === 'puts 1\n' ,
213223 'copied text is trimmed (leading newlines dropped, trailing squeezed)' )
@@ -224,8 +234,14 @@ async function settle() {
224234 const output = new FakeElement ( 'pre' , 'highlight__run-output' )
225235 let current = 'first output\n'
226236 const btn = globalThis . window . ruremaAddCopyButton ( output , ( ) => current )
227- assert ( output . firstChild === btn ,
228- 'the hook prepends a COPY button to the given element' )
237+ assert ( output . firstChild === buttonGroupOf ( output ) &&
238+ buttonGroupOf ( output ) . childByClass ( 'highlight__copy-button' ) === btn ,
239+ 'the hook puts the COPY button into a prepended button group' )
240+ // 既にグループがある要素にもう一度呼んでも、グループは1つのまま再利用される
241+ globalThis . window . ruremaAddCopyButton ( output , ( ) => current )
242+ assert ( output . children . filter (
243+ c => c . className === 'highlight__button-group' ) . length === 1 ,
244+ 'an existing button group is reused instead of stacking a second one' )
229245 btn . onclick ( )
230246 await settle ( )
231247 current = 'second output\n'
@@ -253,7 +269,7 @@ async function settle() {
253269 globalThis . navigator = { } // clipboard なし
254270 ; ( 0 , eval ) ( source )
255271 globalThis . window . onload ( )
256- const btn = pre . childByClass ( 'highlight__copy-button' )
272+ const btn = copyButtonOf ( pre )
257273 btn . onclick ( )
258274 await settle ( )
259275 assert ( captured === 'fallback code\n' ,
0 commit comments