@@ -338,6 +338,103 @@ describe('PAWN_PUSH', () => {
338338 } ) ;
339339} ) ;
340340
341+ // ─── NAG (nag_import) ────────────────────────────────────────────────────────
342+
343+ describe ( 'NAG nag_import' , ( ) => {
344+ it ( 'parses ! as annotation' , ( ) => {
345+ expect ( white ( 'e4!' ) ) . toMatchObject ( {
346+ annotations : [ '!' ] ,
347+ piece : 'P' ,
348+ to : 'e4' ,
349+ } ) ;
350+ } ) ;
351+
352+ it ( 'parses ?? as annotation' , ( ) => {
353+ expect ( white ( 'e4??' ) ) . toMatchObject ( {
354+ annotations : [ '??' ] ,
355+ piece : 'P' ,
356+ to : 'e4' ,
357+ } ) ;
358+ } ) ;
359+
360+ it ( 'parses ± (U+00B1) as annotation' , ( ) => {
361+ expect ( white ( 'e4\u00B1' ) ) . toMatchObject ( {
362+ annotations : [ '\u00B1' ] ,
363+ piece : 'P' ,
364+ to : 'e4' ,
365+ } ) ;
366+ } ) ;
367+
368+ it ( 'parses multiple NAGs on one move' , ( ) => {
369+ expect ( white ( 'e4!$14' ) ) . toMatchObject ( {
370+ annotations : [ '!' , '14' ] ,
371+ piece : 'P' ,
372+ to : 'e4' ,
373+ } ) ;
374+ } ) ;
375+ } ) ;
376+
377+ // ─── COMMENT ─────────────────────────────────────────────────────────────────
378+
379+ describe ( 'COMMENT' , ( ) => {
380+ it ( 'parses a semicolon comment_line' , ( ) => {
381+ const games = parse (
382+ '[Event "T"]\n[Result "1-0"]\n\n1. e4 ; this is a comment\n1-0' ,
383+ ) ;
384+ expect ( games [ 0 ] ?. moves [ 0 ] ?. [ 1 ] ) . toMatchObject ( {
385+ comment : 'this is a comment' ,
386+ piece : 'P' ,
387+ to : 'e4' ,
388+ } ) ;
389+ } ) ;
390+
391+ it ( 'joins multiple comment blocks on one move' , ( ) => {
392+ const games = parse (
393+ '[Event "T"]\n[Result "1-0"]\n\n1. e4 { first } { second } 1-0' ,
394+ ) ;
395+ expect ( games [ 0 ] ?. moves [ 0 ] ?. [ 1 ] ) . toMatchObject ( {
396+ comment : 'first second' ,
397+ piece : 'P' ,
398+ to : 'e4' ,
399+ } ) ;
400+ } ) ;
401+ } ) ;
402+
403+ // ─── RAV ─────────────────────────────────────────────────────────────────────
404+
405+ describe ( 'RAV' , ( ) => {
406+ it ( 'parses a single variation on a move' , ( ) => {
407+ const games = parse (
408+ '[Event "T"]\n[Result "1-0"]\n\n1. e4 (1. d4 d5) e5 1-0' ,
409+ ) ;
410+ const variants = games [ 0 ] ?. moves [ 0 ] ?. [ 1 ] ?. variants ;
411+ expect ( variants ) . toHaveLength ( 1 ) ;
412+ expect ( variants ?. [ 0 ] ?. [ 0 ] ?. [ 1 ] ) . toMatchObject ( { piece : 'P' , to : 'd4' } ) ;
413+ } ) ;
414+
415+ it ( 'parses a nested RAV (RAV inside a RAV)' , ( ) => {
416+ const games = parse (
417+ '[Event "T"]\n[Result "1-0"]\n\n1. e4 (1. d4 (1. c4 c5) d5) e5 1-0' ,
418+ ) ;
419+ const outer = games [ 0 ] ?. moves [ 0 ] ?. [ 1 ] ?. variants ?. [ 0 ] ;
420+ const inner = outer ?. [ 0 ] ?. [ 1 ] ?. variants ;
421+ expect ( inner ) . toHaveLength ( 1 ) ;
422+ expect ( inner ?. [ 0 ] ?. [ 0 ] ?. [ 1 ] ) . toMatchObject ( { piece : 'P' , to : 'c4' } ) ;
423+ } ) ;
424+
425+ it ( 'parses a comment inside a RAV' , ( ) => {
426+ const games = parse (
427+ '[Event "T"]\n[Result "1-0"]\n\n1. e4 (1. d4 { good move } d5) e5 1-0' ,
428+ ) ;
429+ const ravMove = games [ 0 ] ?. moves [ 0 ] ?. [ 1 ] ?. variants ?. [ 0 ] ?. [ 0 ] ?. [ 1 ] ;
430+ expect ( ravMove ) . toMatchObject ( {
431+ comment : 'good move' ,
432+ piece : 'P' ,
433+ to : 'd4' ,
434+ } ) ;
435+ } ) ;
436+ } ) ;
437+
341438// ─── PAWN_CAPTURE ────────────────────────────────────────────────────────────
342439
343440describe ( 'PAWN_CAPTURE' , ( ) => {
0 commit comments