1- import { describe , expect , it , vi } from 'vitest'
1+ import { describe , expect , it , vi , beforeEach } from 'vitest'
2+ import { parsePackageParams } from '../../../../server/utils/parse-package-params'
3+ import { NPM_MISSING_README_SENTINEL } from '#shared/utils/constants'
24
35// Mock Nitro globals before importing the module
46vi . stubGlobal ( 'defineCachedFunction' , ( fn : Function ) => fn )
57const $fetchMock = vi . fn ( )
68vi . stubGlobal ( '$fetch' , $fetchMock )
9+ vi . stubGlobal ( 'parsePackageParams' , parsePackageParams )
710
8- const { fetchReadmeFromJsdelivr, isStandardReadme } =
11+ const fetchNpmPackageMock = vi . fn ( )
12+ vi . stubGlobal ( 'fetchNpmPackage' , fetchNpmPackageMock )
13+
14+ const parseRepositoryInfoMock = vi . fn ( )
15+ vi . stubGlobal ( 'parseRepositoryInfo' , parseRepositoryInfoMock )
16+
17+ const { fetchReadmeFromJsdelivr, isStandardReadme, resolvePackageReadmeSource } =
918 await import ( '../../../../server/utils/readme-loaders' )
1019
1120describe ( 'isStandardReadme' , ( ) => {
@@ -43,20 +52,6 @@ describe('fetchReadmeFromJsdelivr', () => {
4352 expect ( fetchMock ) . toHaveBeenCalledWith ( 'https://cdn.jsdelivr.net/npm/some-pkg/README.md' )
4453 } )
4554
46- it ( 'tries next filename when response is not ok' , async ( ) => {
47- const content = '# Fallback'
48- const fetchMock = vi
49- . fn ( )
50- . mockResolvedValueOnce ( { ok : false } )
51- . mockResolvedValueOnce ( { ok : true , text : async ( ) => content } )
52- vi . stubGlobal ( 'fetch' , fetchMock )
53-
54- const result = await fetchReadmeFromJsdelivr ( 'pkg' , [ 'README.md' , 'readme.md' ] )
55-
56- expect ( result ) . toBe ( content )
57- expect ( fetchMock ) . toHaveBeenCalledTimes ( 2 )
58- } )
59-
6055 it ( 'includes version in URL when version is passed' , async ( ) => {
6156 const fetchMock = vi . fn ( ) . mockResolvedValue ( {
6257 ok : true ,
@@ -79,3 +74,165 @@ describe('fetchReadmeFromJsdelivr', () => {
7974 expect ( fetchMock ) . toHaveBeenCalledTimes ( 2 )
8075 } )
8176} )
77+
78+ describe ( 'resolvePackageReadmeSource' , ( ) => {
79+ beforeEach ( ( ) => {
80+ fetchNpmPackageMock . mockReset ( )
81+ parseRepositoryInfoMock . mockReset ( )
82+ } )
83+
84+ it ( 'returns markdown and repoInfo when package has valid npm readme (latest)' , async ( ) => {
85+ const markdown = '# Hello'
86+ fetchNpmPackageMock . mockResolvedValue ( {
87+ readme : markdown ,
88+ readmeFilename : 'README.md' ,
89+ repository : { url : 'https://github.com/u/r' } ,
90+ versions : { } ,
91+ } )
92+ parseRepositoryInfoMock . mockReturnValue ( {
93+ provider : 'github' ,
94+ owner : 'u' ,
95+ repo : 'r' ,
96+ rawBaseUrl : 'https://raw.githubusercontent.com/u/r/HEAD' ,
97+ blobBaseUrl : 'https://github.com/u/r/blob/HEAD' ,
98+ } )
99+
100+ const result = await resolvePackageReadmeSource ( 'some-pkg' )
101+
102+ expect ( result ) . toMatchObject ( {
103+ packageName : 'some-pkg' ,
104+ version : undefined ,
105+ markdown,
106+ repoInfo : { provider : 'github' , owner : 'u' , repo : 'r' } ,
107+ } )
108+ expect ( fetchNpmPackageMock ) . toHaveBeenCalledWith ( 'some-pkg' )
109+ } )
110+
111+ it ( 'returns markdown from version when packagePath includes version' , async ( ) => {
112+ const markdown = '# Version readme'
113+ fetchNpmPackageMock . mockResolvedValue ( {
114+ readme : 'latest readme' ,
115+ readmeFilename : 'README.md' ,
116+ repository : undefined ,
117+ versions : {
118+ '1.0.0' : { readme : markdown , readmeFilename : 'README.md' } ,
119+ } ,
120+ } )
121+ parseRepositoryInfoMock . mockReturnValue ( undefined )
122+
123+ const result = await resolvePackageReadmeSource ( 'some-pkg/v/1.0.0' )
124+
125+ expect ( result ) . toMatchObject ( {
126+ packageName : 'some-pkg' ,
127+ version : '1.0.0' ,
128+ markdown,
129+ } )
130+ } )
131+
132+ it ( 'falls back to jsdelivr when npm readme is missing sentinel' , async ( ) => {
133+ const jsdelivrContent = '# From CDN'
134+ fetchNpmPackageMock . mockResolvedValue ( {
135+ readme : NPM_MISSING_README_SENTINEL ,
136+ readmeFilename : 'README.md' ,
137+ repository : undefined ,
138+ versions : { } ,
139+ } )
140+ parseRepositoryInfoMock . mockReturnValue ( undefined )
141+ const fetchMock = vi . fn ( ) . mockResolvedValue ( {
142+ ok : true ,
143+ text : async ( ) => jsdelivrContent ,
144+ } )
145+ vi . stubGlobal ( 'fetch' , fetchMock )
146+
147+ const result = await resolvePackageReadmeSource ( 'pkg' )
148+
149+ expect ( result ) . toMatchObject ( {
150+ packageName : 'pkg' ,
151+ markdown : jsdelivrContent ,
152+ repoInfo : undefined ,
153+ } )
154+ expect ( fetchMock ) . toHaveBeenCalled ( )
155+ } )
156+
157+ it ( 'falls back to jsdelivr when readmeFilename is not standard' , async ( ) => {
158+ const jsdelivrContent = '# From CDN'
159+ fetchNpmPackageMock . mockResolvedValue ( {
160+ readme : 'content' ,
161+ readmeFilename : 'DOCS.md' ,
162+ repository : undefined ,
163+ versions : { } ,
164+ } )
165+ parseRepositoryInfoMock . mockReturnValue ( undefined )
166+ const fetchMock = vi . fn ( ) . mockResolvedValue ( {
167+ ok : true ,
168+ text : async ( ) => jsdelivrContent ,
169+ } )
170+ vi . stubGlobal ( 'fetch' , fetchMock )
171+
172+ const result = await resolvePackageReadmeSource ( 'pkg' )
173+
174+ expect ( result ) . toMatchObject ( { markdown : jsdelivrContent } )
175+ } )
176+
177+ it ( 'returns undefined markdown when no content and jsdelivr fails' , async ( ) => {
178+ fetchNpmPackageMock . mockResolvedValue ( {
179+ readme : undefined ,
180+ readmeFilename : undefined ,
181+ repository : undefined ,
182+ versions : { } ,
183+ } )
184+ parseRepositoryInfoMock . mockReturnValue ( undefined )
185+ const fetchMock = vi . fn ( ) . mockResolvedValue ( { ok : false } )
186+ vi . stubGlobal ( 'fetch' , fetchMock )
187+
188+ const result = await resolvePackageReadmeSource ( 'pkg' )
189+
190+ expect ( result ) . toMatchObject ( {
191+ packageName : 'pkg' ,
192+ version : undefined ,
193+ markdown : undefined ,
194+ repoInfo : undefined ,
195+ } )
196+ } )
197+
198+ it ( 'returns undefined markdown when content is NPM_MISSING_README_SENTINEL and jsdelivr fails' , async ( ) => {
199+ fetchNpmPackageMock . mockResolvedValue ( {
200+ readme : NPM_MISSING_README_SENTINEL ,
201+ readmeFilename : 'README.md' ,
202+ repository : undefined ,
203+ versions : { } ,
204+ } )
205+ const fetchMock = vi . fn ( ) . mockResolvedValue ( { ok : false } )
206+ vi . stubGlobal ( 'fetch' , fetchMock )
207+
208+ const result = await resolvePackageReadmeSource ( 'pkg' )
209+
210+ expect ( result ) . toMatchObject ( {
211+ packageName : 'pkg' ,
212+ markdown : undefined ,
213+ repoInfo : undefined ,
214+ } )
215+ } )
216+
217+ it ( 'uses package repository for repoInfo when markdown is present' , async ( ) => {
218+ fetchNpmPackageMock . mockResolvedValue ( {
219+ readme : '# Hi' ,
220+ readmeFilename : 'README.md' ,
221+ repository : { url : 'https://github.com/a/b' } ,
222+ versions : { } ,
223+ } )
224+ const repoInfo = {
225+ provider : 'github' as const ,
226+ owner : 'a' ,
227+ repo : 'b' ,
228+ rawBaseUrl : 'https://raw.githubusercontent.com/a/b/HEAD' ,
229+ blobBaseUrl : 'https://github.com/a/b/blob/HEAD' ,
230+ }
231+ parseRepositoryInfoMock . mockReturnValue ( repoInfo )
232+
233+ const result = await resolvePackageReadmeSource ( 'pkg' )
234+
235+ expect ( result ?. repoInfo ) . toEqual ( repoInfo )
236+ expect ( parseRepositoryInfoMock ) . toHaveBeenCalledWith ( { url : 'https://github.com/a/b' } )
237+ } )
238+ } )
0 commit comments