@@ -3,6 +3,33 @@ import { IPlugin } from "./pluginTypes";
33import { setLogLevel } from "../log" ;
44import { blocksToMarkdown } from "./pluginTestRun" ;
55import { gifEmbed , imgurGifEmbed } from "./embedTweaks" ;
6+ import { standardExternalLinkConversion } from "./externalLinks" ;
7+ import defaultConfig from "../config/default.docunotion.config" ;
8+ import { NotionBlock as NB } from "../types" ;
9+
10+ function paragraph ( text : string ) : NB {
11+ return {
12+ type : "paragraph" ,
13+ paragraph : {
14+ rich_text : [
15+ {
16+ type : "text" ,
17+ text : { content : text , link : null } ,
18+ annotations : {
19+ bold : false ,
20+ italic : false ,
21+ strikethrough : false ,
22+ underline : false ,
23+ code : false ,
24+ color : "default" ,
25+ } ,
26+ plain_text : text ,
27+ href : null ,
28+ } ,
29+ ] ,
30+ } ,
31+ } as unknown as NB ;
32+ }
633
734test ( "imgur" , async ( ) => {
835 setLogLevel ( "verbose" ) ;
@@ -18,6 +45,104 @@ test("imgur", async () => {
1845 expect ( result . trim ( ) ) . toBe ( `` ) ;
1946} ) ;
2047
48+ // Regression test for the "!" bug: with both plugins active (the
49+ // default config order), the imgur mod turns a bare imgur link into an image,
50+ // then the gif mod sees that ".gif" image and must not prepend a second "!".
51+ test ( "imgur + gif together produce a single leading bang" , async ( ) => {
52+ setLogLevel ( "verbose" ) ;
53+ const config = { plugins : [ imgurGifEmbed , gifEmbed ] } ;
54+ const result = await blocksToMarkdown ( config , [
55+ {
56+ object : "block" ,
57+ id : "e36710d8-98ad-40dc-b41b-b376ebdd6894" ,
58+ type : "bookmark" ,
59+ bookmark : { caption : [ ] , url : "https://imgur.com/E83qLj6" } ,
60+ } as unknown as NotionBlock ,
61+ ] ) ;
62+ expect ( result . trim ( ) ) . toBe ( `` ) ;
63+ expect ( result ) . not . toContain ( "!![]" ) ;
64+ } ) ;
65+
66+ // An inline link the user gave real text to must stay a clickable link: we must
67+ // not turn it into an image (which also discards the text). Uses the default
68+ // config order so both mods get a crack at it.
69+ test ( "imgur link with author text is left as a clickable link" , async ( ) => {
70+ setLogLevel ( "verbose" ) ;
71+ const config = { plugins : [ imgurGifEmbed , gifEmbed ] } ;
72+ const result = await blocksToMarkdown ( config , [
73+ paragraph (
74+ "all at once ([see animation](https://imgur.com/gcrxl5k))."
75+ ) ,
76+ paragraph (
77+ "(See an animation of these [new overlay features](https://imgur.com/E83qLj6))"
78+ ) ,
79+ ] ) ;
80+ expect ( result ) . toContain ( "[see animation](https://imgur.com/gcrxl5k)" ) ;
81+ expect ( result ) . toContain (
82+ "[new overlay features](https://imgur.com/E83qLj6)"
83+ ) ;
84+ // nothing should have been turned into an image
85+ expect ( result ) . not . toContain ( "![]" ) ;
86+ expect ( result ) . not . toContain ( ".gif" ) ;
87+ } ) ;
88+
89+ // Production-realistic: standardExternalLinkConversion runs in an earlier phase
90+ // and rewrites a Notion `[bookmark](url)` into `[url](url)` before the embed
91+ // regexes run. The embed must still recognize that as an auto-label and embed it.
92+ test ( "imgur bookmark still embeds after external-link conversion" , async ( ) => {
93+ setLogLevel ( "verbose" ) ;
94+ const config = {
95+ plugins : [ standardExternalLinkConversion , imgurGifEmbed , gifEmbed ] ,
96+ } ;
97+ const result = await blocksToMarkdown ( config , [
98+ {
99+ object : "block" ,
100+ id : "e36710d8-98ad-40dc-b41b-b376ebdd6894" ,
101+ type : "bookmark" ,
102+ bookmark : { caption : [ ] , url : "https://imgur.com/gallery/U8TTNuI" } ,
103+ } as unknown as NotionBlock ,
104+ ] ) ;
105+ expect ( result . trim ( ) ) . toBe ( `` ) ;
106+ expect ( result ) . not . toContain ( "!![]" ) ;
107+ } ) ;
108+
109+ // ...but an authored inline link must survive that same pipeline as a link.
110+ test ( "authored imgur link survives external-link conversion as a link" , async ( ) => {
111+ setLogLevel ( "verbose" ) ;
112+ const config = {
113+ plugins : [ standardExternalLinkConversion , imgurGifEmbed , gifEmbed ] ,
114+ } ;
115+ const result = await blocksToMarkdown ( config , [
116+ paragraph ( "all at once ([see animation](https://imgur.com/gcrxl5k))." ) ,
117+ ] ) ;
118+ expect ( result ) . toContain ( "[see animation](https://imgur.com/gcrxl5k)" ) ;
119+ expect ( result ) . not . toContain ( "![]" ) ;
120+ expect ( result ) . not . toContain ( ".gif" ) ;
121+ } ) ;
122+
123+ // Strongest guard: run the ACTUAL default production config (full plugin set
124+ // and order) rather than a hand-picked subset, so future config drift (plugin
125+ // reordering, the external-link converter changing) can't silently reopen the
126+ // bug. A Notion bookmark to imgur must embed; an authored inline link must stay
127+ // a clickable link.
128+ test ( "default production config: bookmark embeds, authored link stays a link" , async ( ) => {
129+ setLogLevel ( "verbose" ) ;
130+ const result = await blocksToMarkdown ( defaultConfig , [
131+ {
132+ object : "block" ,
133+ id : "e36710d8-98ad-40dc-b41b-b376ebdd6894" ,
134+ type : "bookmark" ,
135+ bookmark : { caption : [ ] , url : "https://imgur.com/gallery/U8TTNuI" } ,
136+ } as unknown as NotionBlock ,
137+ paragraph ( "all at once ([see animation](https://imgur.com/gcrxl5k))." ) ,
138+ ] ) ;
139+ // the bookmark became an embedded gif...
140+ expect ( result ) . toContain ( "" ) ;
141+ expect ( result ) . not . toContain ( "!![]" ) ;
142+ // ...while the authored link is untouched
143+ expect ( result ) . toContain ( "[see animation](https://imgur.com/gcrxl5k)" ) ;
144+ } ) ;
145+
21146test ( "gif" , async ( ) => {
22147 setLogLevel ( "verbose" ) ;
23148 const config = { plugins : [ gifEmbed ] } ;
0 commit comments