44 Notification ,
55 Paragraph ,
66 _setModelData as setModelData ,
7- _getModelData as getModelData ,
87} from "ckeditor5" ;
98import { afterEach , beforeEach , describe , expect , it , vi } from "vitest" ;
109import FormatCodeblockButton from "../src/plugins/format_codeblock/format_codeblock_button" ;
@@ -58,6 +57,23 @@ function setCodeBlockContent(
5857 } ) ;
5958}
6059
60+ /**
61+ * Extract plain text from the first codeBlock element in the editor model.
62+ * Mirrors FormatCodeblockCommand.extractCodeText so assertions test the same
63+ * value the formatter operates on.
64+ */
65+ function getCodeBlockText ( editor : ClassicEditor ) : string | undefined {
66+ const root = editor . model . document . getRoot ( ) ! ;
67+ for ( const child of root . getChildren ( ) ) {
68+ if ( child . is ( "element" , "codeBlock" ) ) {
69+ return Array . from ( child . getChildren ( ) )
70+ . map ( ( c ) => ( c . is ( "$text" ) ? c . data : "\n" ) )
71+ . join ( "" ) ;
72+ }
73+ }
74+ return undefined ;
75+ }
76+
6177describe ( "FormatCodeblockButton" , ( ) => {
6278 let domElement : HTMLDivElement ;
6379 let editor : ClassicEditor ;
@@ -123,7 +139,6 @@ describe("FormatCodeblockButton", () => {
123139 LANG_SCSS ,
124140 LANG_LESS ,
125141 LANG_HTML ,
126- LANG_XML ,
127142 LANG_YAML ,
128143 LANG_MARKDOWN ,
129144 LANG_GRAPHQL ,
@@ -146,7 +161,7 @@ describe("FormatCodeblockButton", () => {
146161 } ) ;
147162
148163 describe ( "should be disabled for unsupported languages" , ( ) => {
149- const unsupportedLanguages = [ LANG_PYTHON , LANG_RUST , LANG_C ] ;
164+ const unsupportedLanguages = [ LANG_XML , LANG_PYTHON , LANG_RUST , LANG_C ] ;
150165
151166 for ( const lang of unsupportedLanguages ) {
152167 it ( `should be disabled for "${ lang } "` , ( ) => {
@@ -173,20 +188,19 @@ describe("FormatCodeblockButton", () => {
173188
174189 describe ( "FormatCodeblockCommand#execute" , ( ) => {
175190 it ( "should format JavaScript code" , async ( ) => {
176- setModelData (
177- editor . model ,
178- `<codeBlock language="${ LANG_JAVASCRIPT_FRONTEND } ">const x=1;const y=2;const z=x+y[]</codeBlock>` ,
191+ setCodeBlockContent (
192+ editor ,
193+ LANG_JAVASCRIPT_FRONTEND ,
194+ "const x=1;const y=2;const z=x+y" ,
179195 ) ;
180196
181197 editor . execute ( "formatCodeblock" ) ;
182198
183199 await vi . waitFor (
184200 ( ) => {
185- const modelData = getModelData ( editor . model , {
186- withoutSelection : true ,
187- } ) ;
188- expect ( modelData ) . toEqual (
189- "const x = 1;\nconst y = 2;\nconst z = x + y;" ,
201+ const text = getCodeBlockText ( editor ) ;
202+ expect ( text ) . toEqual (
203+ "const x = 1;\nconst y = 2;\nconst z = x + y;\n" ,
190204 ) ;
191205 } ,
192206 { timeout : 10000 } ,
@@ -204,11 +218,9 @@ describe("FormatCodeblockButton", () => {
204218
205219 await vi . waitFor (
206220 ( ) => {
207- const modelData = getModelData ( editor . model , {
208- withoutSelection : true ,
209- } ) ;
210- expect ( modelData ) . toEqual (
211- '{\n "a": 1,\n "b": 2,\n "c": [\n 1,\n 2,\n 3\n ]\n}' ,
221+ const text = getCodeBlockText ( editor ) ;
222+ expect ( text ) . toEqual (
223+ '{ "a": 1, "b": 2, "c": [1, 2, 3] }\n' ,
212224 ) ;
213225 } ,
214226 { timeout : 10000 } ,
@@ -226,11 +238,9 @@ describe("FormatCodeblockButton", () => {
226238
227239 await vi . waitFor (
228240 ( ) => {
229- const modelData = getModelData ( editor . model , {
230- withoutSelection : true ,
231- } ) ;
232- expect ( modelData ) . toEqual (
233- "body {\n color: red;\n background: blue;\n}" ,
241+ const text = getCodeBlockText ( editor ) ;
242+ expect ( text ) . toEqual (
243+ "body {\n color: red;\n background: blue;\n}\n" ,
234244 ) ;
235245 } ,
236246 { timeout : 10000 } ,
@@ -248,88 +258,65 @@ describe("FormatCodeblockButton", () => {
248258
249259 await vi . waitFor (
250260 ( ) => {
251- const modelData = getModelData ( editor . model , {
252- withoutSelection : true ,
253- } ) ;
254- expect ( modelData ) . toEqual (
255- "interface Foo {\n bar: string;\n baz: number;\n}" ,
261+ const text = getCodeBlockText ( editor ) ;
262+ expect ( text ) . toEqual (
263+ "interface Foo {\n bar: string;\n baz: number;\n}\n" ,
256264 ) ;
257265 } ,
258266 { timeout : 10000 } ,
259267 ) ;
260268 } ) ;
261269
262270 it ( "should not modify already-formatted code" , async ( ) => {
263- const formattedCode = "const x = 1;" ;
264- setModelData (
265- editor . model ,
266- `<codeBlock language="${ LANG_JAVASCRIPT_FRONTEND } ">${ formattedCode } []</codeBlock>` ,
271+ // Prettier always appends a trailing newline, so the input must
272+ // already include one (represented as a softBreak) to be truly
273+ // "already formatted".
274+ setCodeBlockContent (
275+ editor ,
276+ LANG_JAVASCRIPT_FRONTEND ,
277+ "const x = 1;\n" ,
267278 ) ;
268279
269- const modelDataBefore = getModelData ( editor . model , {
270- withoutSelection : true ,
271- } ) ;
280+ const textBefore = getCodeBlockText ( editor ) ;
272281
273282 editor . execute ( "formatCodeblock" ) ;
274283
284+ // Give the async formatter a chance to run, then verify no change.
275285 await vi . waitFor ( ( ) => {
276- const modelDataAfter = getModelData ( editor . model , {
277- withoutSelection : true ,
278- } ) ;
279- expect ( modelDataAfter ) . toBe ( modelDataBefore ) ;
286+ const textAfter = getCodeBlockText ( editor ) ;
287+ expect ( textAfter ) . toBe ( textBefore ) ;
280288 } ) ;
281289
282- const modelDataAfter = getModelData ( editor . model , {
283- withoutSelection : true ,
284- } ) ;
285- expect ( modelDataAfter ) . toBe ( modelDataBefore ) ;
290+ const textAfter = getCodeBlockText ( editor ) ;
291+ expect ( textAfter ) . toBe ( textBefore ) ;
286292 } ) ;
287293
288294 it ( "should not modify empty code blocks" , ( ) => {
289- setModelData (
290- editor . model ,
291- `<codeBlock language="${ LANG_JAVASCRIPT_FRONTEND } ">[]</codeBlock>` ,
292- ) ;
295+ setCodeBlockContent ( editor , LANG_JAVASCRIPT_FRONTEND , "" ) ;
293296
294- const modelDataBefore = getModelData ( editor . model , {
295- withoutSelection : true ,
296- } ) ;
297+ const textBefore = getCodeBlockText ( editor ) ;
297298
298299 editor . execute ( "formatCodeblock" ) ;
299300
300- const modelDataAfter = getModelData ( editor . model , {
301- withoutSelection : true ,
302- } ) ;
303- expect ( modelDataAfter ) . toBe ( modelDataBefore ) ;
301+ const textAfter = getCodeBlockText ( editor ) ;
302+ expect ( textAfter ) . toBe ( textBefore ) ;
304303 } ) ;
305304
306305 it ( "should not modify whitespace-only code blocks" , ( ) => {
307- setModelData (
308- editor . model ,
309- `<codeBlock language="${ LANG_JAVASCRIPT_FRONTEND } "> []</codeBlock>` ,
310- ) ;
306+ setCodeBlockContent ( editor , LANG_JAVASCRIPT_FRONTEND , " " ) ;
311307
312- const modelDataBefore = getModelData ( editor . model , {
313- withoutSelection : true ,
314- } ) ;
308+ const textBefore = getCodeBlockText ( editor ) ;
315309
316310 editor . execute ( "formatCodeblock" ) ;
317311
318- const modelDataAfter = getModelData ( editor . model , {
319- withoutSelection : true ,
320- } ) ;
321- expect ( modelDataAfter ) . toBe ( modelDataBefore ) ;
312+ const textAfter = getCodeBlockText ( editor ) ;
313+ expect ( textAfter ) . toBe ( textBefore ) ;
322314 } ) ;
323315
324316 it ( "should not execute when language is unsupported" , ( ) => {
325- setModelData (
326- editor . model ,
327- `<codeBlock language="${ LANG_PYTHON } ">x=1[]</codeBlock>` ,
328- ) ;
317+ setCodeBlockContent ( editor , LANG_PYTHON , "x=1" ) ;
329318
330- const modelDataBefore = getModelData ( editor . model , {
331- withoutSelection : true ,
332- } ) ;
319+ const textBefore = getCodeBlockText ( editor ) ;
333320
334321 // Manually try to execute — the command should be disabled, so
335322 // calling execute directly on the command should be a no-op.
@@ -338,57 +325,41 @@ describe("FormatCodeblockButton", () => {
338325 ) ! as FormatCodeblockCommand ;
339326 command . execute ( ) ;
340327
341- const modelDataAfter = getModelData ( editor . model , {
342- withoutSelection : true ,
343- } ) ;
344- expect ( modelDataAfter ) . toBe ( modelDataBefore ) ;
328+ const textAfter = getCodeBlockText ( editor ) ;
329+ expect ( textAfter ) . toBe ( textBefore ) ;
345330 } ) ;
346331
347332 it ( "should handle multiline code with softBreaks in the model" , async ( ) => {
348- editor . model . change ( ( writer ) => {
349- const root = editor . model . document . getRoot ( ) ! ;
350-
351- writer . remove ( writer . createRangeIn ( root ) ) ;
352-
353- const codeBlock = writer . createElement ( "codeBlock" , {
354- language : LANG_JAVASCRIPT_FRONTEND ,
355- } ) ;
356- writer . appendText ( "const x=1;" , codeBlock ) ;
357- writer . appendElement ( "softBreak" , codeBlock ) ;
358- writer . appendText ( "const y=2;" , codeBlock ) ;
359-
360- writer . append ( codeBlock , root ) ;
361-
362- writer . setSelection ( codeBlock , "end" ) ;
363- } ) ;
333+ setCodeBlockContent (
334+ editor ,
335+ LANG_JAVASCRIPT_FRONTEND ,
336+ "const x=1;\nconst y=2;" ,
337+ ) ;
364338
365339 editor . execute ( "formatCodeblock" ) ;
366340
367341 await vi . waitFor (
368342 ( ) => {
369- const modelData = getModelData ( editor . model , {
370- withoutSelection : true ,
371- } ) ;
372- expect ( modelData ) . toEqual ( "const x = 1;\nconst y = 2;" ) ;
343+ const text = getCodeBlockText ( editor ) ;
344+ expect ( text ) . toEqual ( "const x = 1;\nconst y = 2;\n" ) ;
373345 } ,
374346 { timeout : 10000 } ,
375347 ) ;
376348 } ) ;
377349
378350 it ( "should format YAML code" , async ( ) => {
379- setModelData (
380- editor . model ,
381- `<codeBlock language="${ LANG_YAML } ">foo: bar\nbaz: qux[]</codeBlock>` ,
351+ setCodeBlockContent (
352+ editor ,
353+ LANG_YAML ,
354+ "foo: bar\nbaz: qux" ,
382355 ) ;
383356
384357 editor . execute ( "formatCodeblock" ) ;
385358
386359 await vi . waitFor (
387360 ( ) => {
388- const modelData = getModelData ( editor . model , {
389- withoutSelection : true ,
390- } ) ;
391- expect ( modelData ) . toEqual ( "foo: bar\nbaz: qux" ) ;
361+ const text = getCodeBlockText ( editor ) ;
362+ expect ( text ) . toEqual ( "foo: bar\nbaz: qux\n" ) ;
392363 } ,
393364 { timeout : 10000 } ,
394365 ) ;
0 commit comments