@@ -19,9 +19,12 @@ async function startMermaidMock({ status = 200, body = TINY_PNG, svgBody = TINY_
1919 return await mockHttpServer ( {
2020 routes : {
2121 '/img/' : ( req , res ) => {
22- // path 形如 /img/<base64url>
23- const segment = decodeURIComponent ( req . url . slice ( '/img/' . length ) )
24- // 把 base64url 还原成 base64
22+ // path 形如 /img/<base64url>?type=png&width=...&scale=...
23+ // 先剥离查询串再 base64url 解码
24+ const afterPrefix = req . url . slice ( '/img/' . length )
25+ const qIdx = afterPrefix . indexOf ( '?' )
26+ const segmentRaw = qIdx === - 1 ? afterPrefix : afterPrefix . slice ( 0 , qIdx )
27+ const segment = decodeURIComponent ( segmentRaw )
2528 const std = segment . replace ( / - / g, '+' ) . replace ( / _ / g, '/' )
2629 const padded = std + '==' . slice ( ( std . length + 2 ) % 4 )
2730 let decoded
@@ -214,6 +217,81 @@ describe('render-mermaid.mjs', () => {
214217 }
215218 } )
216219
220+ it ( '默认调用带 width=1600 scale=2 高清参数' , async ( ) => {
221+ const mock = await startMermaidMock ( )
222+ try {
223+ await withTempDir ( async ( dir ) => {
224+ const { exitCode } = await runScript ( SCRIPT , [
225+ '--inline' , 'graph TD; A-->B' ,
226+ '--output-dir' , dir
227+ ] , { env : { MERMAID_INK_URL : mock . url } } )
228+
229+ assert . equal ( exitCode , 0 )
230+ const imgReq = mock . requests . find ( ( r ) => r . url && r . url . startsWith ( '/img/' ) )
231+ assert . ok ( imgReq , '应有 /img/ 请求' )
232+ assert . match ( imgReq . url , / [ ? & ] w i d t h = 1 6 0 0 / )
233+ assert . match ( imgReq . url , / [ ? & ] s c a l e = 2 / )
234+ } )
235+ } finally {
236+ await mock . close ( )
237+ }
238+ } )
239+
240+ it ( '--scale 3 --width 2000 透传到 URL' , async ( ) => {
241+ const mock = await startMermaidMock ( )
242+ try {
243+ await withTempDir ( async ( dir ) => {
244+ const { exitCode } = await runScript ( SCRIPT , [
245+ '--inline' , 'graph TD; A-->B' ,
246+ '--output-dir' , dir ,
247+ '--scale' , '3' ,
248+ '--width' , '2000'
249+ ] , { env : { MERMAID_INK_URL : mock . url } } )
250+
251+ assert . equal ( exitCode , 0 )
252+ const imgReq = mock . requests . find ( ( r ) => r . url && r . url . startsWith ( '/img/' ) )
253+ assert . match ( imgReq . url , / [ ? & ] w i d t h = 2 0 0 0 / )
254+ assert . match ( imgReq . url , / [ ? & ] s c a l e = 3 / )
255+ } )
256+ } finally {
257+ await mock . close ( )
258+ }
259+ } )
260+
261+ it ( '--scale 越界(如 5)报错退出' , async ( ) => {
262+ await withTempDir ( async ( dir ) => {
263+ const { exitCode, lastJsonLine } = await runScript ( SCRIPT , [
264+ '--inline' , 'graph TD; A-->B' ,
265+ '--output-dir' , dir ,
266+ '--scale' , '5'
267+ ] , { env : { MERMAID_INK_URL : 'http://127.0.0.1:1' } } )
268+
269+ assert . equal ( exitCode , 1 )
270+ assert . equal ( lastJsonLine . ok , false )
271+ assert . match ( lastJsonLine . error . message , / s c a l e / i)
272+ } )
273+ } )
274+
275+ it ( 'SVG 输出不带 width/scale 查询参数' , async ( ) => {
276+ const mock = await startMermaidMock ( )
277+ try {
278+ await withTempDir ( async ( dir ) => {
279+ const { exitCode } = await runScript ( SCRIPT , [
280+ '--inline' , 'graph TD; A-->B' ,
281+ '--output-dir' , dir ,
282+ '--format' , 'svg'
283+ ] , { env : { MERMAID_INK_URL : mock . url } } )
284+
285+ assert . equal ( exitCode , 0 )
286+ const svgReq = mock . requests . find ( ( r ) => r . url && r . url . startsWith ( '/svg/' ) )
287+ assert . ok ( svgReq )
288+ assert . ok ( ! svgReq . url . includes ( '?' ) , 'svg 不应带查询参数' )
289+ } )
290+ } finally {
291+ await mock . close ( )
292+ }
293+ } )
294+
217295 it ( 'base64url 编码正确:mock 端能 decode 回原文' , async ( ) => {
218296 const mock = await startMermaidMock ( )
219297 try {
@@ -228,7 +306,11 @@ describe('render-mermaid.mjs', () => {
228306 // 找到 /img/ 请求并断言解码回的原文等于 inline
229307 const imgReq = mock . requests . find ( ( r ) => r . url && r . url . startsWith ( '/img/' ) )
230308 assert . ok ( imgReq , '应当至少有一次 /img/ 请求' )
231- const segment = decodeURIComponent ( imgReq . url . slice ( '/img/' . length ) )
309+ // 剥离查询串再解码(高清参数化后 URL 形如 /img/<b64>?type=png&width=...&scale=...)
310+ const afterPrefix = imgReq . url . slice ( '/img/' . length )
311+ const qIdx = afterPrefix . indexOf ( '?' )
312+ const segmentRaw = qIdx === - 1 ? afterPrefix : afterPrefix . slice ( 0 , qIdx )
313+ const segment = decodeURIComponent ( segmentRaw )
232314 const std = segment . replace ( / - / g, '+' ) . replace ( / _ / g, '/' )
233315 const padded = std + '==' . slice ( ( std . length + 2 ) % 4 )
234316 const decoded = Buffer . from ( padded , 'base64' ) . toString ( 'utf8' )
0 commit comments