@@ -2,21 +2,59 @@ const t = require('tap')
22const { basename } = require ( 'node:path' )
33const tmock = require ( '../../fixtures/tmock' )
44const mockNpm = require ( '../../fixtures/mock-npm' )
5+ const MockRegistry = require ( '@npmcli/mock-registry' )
6+ const mockGlobals = require ( '@npmcli/mock-globals' )
57
68const CURRENT_VERSION = '123.420.69'
79const CURRENT_MAJOR = '122.420.69'
810const CURRENT_MINOR = '123.419.69'
911const CURRENT_PATCH = '123.420.68'
1012const NEXT_VERSION = '123.421.70'
13+ const NEXT_VERSION_ENGINE_COMPATIBLE = '123.421.60'
14+ const NEXT_VERSION_ENGINE_COMPATIBLE_MINOR = `123.420.70`
15+ const NEXT_VERSION_ENGINE_COMPATIBLE_PATCH = `123.421.58`
1116const NEXT_MINOR = '123.420.70'
1217const NEXT_PATCH = '123.421.69'
1318const CURRENT_BETA = '124.0.0-beta.99999'
1419const HAVE_BETA = '124.0.0-beta.0'
1520
21+ const packumentResponse = {
22+ _id : 'npm' ,
23+ name : 'npm' ,
24+ 'dist-tags' : {
25+ latest : CURRENT_VERSION ,
26+ } ,
27+ access : 'public' ,
28+ versions : {
29+ [ CURRENT_VERSION ] : { version : CURRENT_VERSION , engines : { node : '>1' } } ,
30+ [ CURRENT_MAJOR ] : { version : CURRENT_MAJOR , engines : { node : '>1' } } ,
31+ [ CURRENT_MINOR ] : { version : CURRENT_MINOR , engines : { node : '>1' } } ,
32+ [ CURRENT_PATCH ] : { version : CURRENT_PATCH , engines : { node : '>1' } } ,
33+ [ NEXT_VERSION ] : { version : NEXT_VERSION , engines : { node : '>1' } } ,
34+ [ NEXT_MINOR ] : { version : NEXT_MINOR , engines : { node : '>1' } } ,
35+ [ NEXT_PATCH ] : { version : NEXT_PATCH , engines : { node : '>1' } } ,
36+ [ CURRENT_BETA ] : { version : CURRENT_BETA , engines : { node : '>1' } } ,
37+ [ HAVE_BETA ] : { version : HAVE_BETA , engines : { node : '>1' } } ,
38+ [ NEXT_VERSION_ENGINE_COMPATIBLE ] : {
39+ version : NEXT_VERSION_ENGINE_COMPATIBLE ,
40+ engiges : { node : '<=1' } ,
41+ } ,
42+ [ NEXT_VERSION_ENGINE_COMPATIBLE_MINOR ] : {
43+ version : NEXT_VERSION_ENGINE_COMPATIBLE_MINOR ,
44+ engines : { node : '<=1' } ,
45+ } ,
46+ [ NEXT_VERSION_ENGINE_COMPATIBLE_PATCH ] : {
47+ version : NEXT_VERSION_ENGINE_COMPATIBLE_PATCH ,
48+ engines : { node : '<=1' } ,
49+ } ,
50+ } ,
51+ }
52+
1653const runUpdateNotifier = async ( t , {
1754 STAT_ERROR ,
1855 WRITE_ERROR ,
1956 PACOTE_ERROR ,
57+ PACOTE_MOCK_REQ_COUNT = 1 ,
2058 STAT_MTIME = 0 ,
2159 mocks : _mocks = { } ,
2260 command = 'help' ,
@@ -51,24 +89,7 @@ const runUpdateNotifier = async (t, {
5189 } ,
5290 }
5391
54- const MANIFEST_REQUEST = [ ]
55- const mockPacote = {
56- manifest : async ( spec ) => {
57- if ( ! spec . match ( / ^ n p m @ / ) ) {
58- t . fail ( 'no pacote manifest allowed for non npm packages' )
59- }
60- MANIFEST_REQUEST . push ( spec )
61- if ( PACOTE_ERROR ) {
62- throw PACOTE_ERROR
63- }
64- const manifestV = spec === 'npm@latest' ? CURRENT_VERSION
65- : / - / . test ( spec ) ? CURRENT_BETA : NEXT_VERSION
66- return { version : manifestV }
67- } ,
68- }
69-
7092 const mocks = {
71- pacote : mockPacote ,
7293 'node:fs/promises' : mockFs ,
7394 '{ROOT}/package.json' : { version } ,
7495 'ci-info' : { isCI : false , name : null } ,
@@ -83,124 +104,125 @@ const runUpdateNotifier = async (t, {
83104 prefixDir,
84105 argv,
85106 } )
107+ const registry = new MockRegistry ( {
108+ tap : t ,
109+ registry : mock . npm . config . get ( 'registry' ) ,
110+ } )
111+
112+ if ( PACOTE_MOCK_REQ_COUNT > 0 ) {
113+ registry . nock . get ( '/npm' ) . times ( PACOTE_MOCK_REQ_COUNT ) . reply ( 200 , packumentResponse )
114+ }
115+
86116 const updateNotifier = tmock ( t , '{LIB}/cli/update-notifier.js' , mocks )
87117
88118 const result = await updateNotifier ( mock . npm )
89119
90120 return {
91121 wroteFile,
92122 result,
93- MANIFEST_REQUEST ,
94123 }
95124}
96125
97126t . test ( 'duration has elapsed, no updates' , async t => {
98- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t )
127+ const { wroteFile, result } = await runUpdateNotifier ( t )
99128 t . equal ( wroteFile , true )
100129 t . not ( result )
101- t . equal ( MANIFEST_REQUEST . length , 1 )
102130} )
103131
104132t . test ( 'situations in which we do not notify' , t => {
105133 t . test ( 'nothing to do if notifier disabled' , async t => {
106- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , {
134+ const { wroteFile, result } = await runUpdateNotifier ( t , {
135+ PACOTE_MOCK_REQ_COUNT : 0 ,
107136 'update-notifier' : false ,
108137 } )
109138 t . equal ( wroteFile , false )
110139 t . equal ( result , null )
111- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
112140 } )
113141
114142 t . test ( 'do not suggest update if already updating' , async t => {
115- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , {
143+ const { wroteFile, result } = await runUpdateNotifier ( t , {
144+ PACOTE_MOCK_REQ_COUNT : 0 ,
116145 command : 'install' ,
117146 prefixDir : { 'package.json' : `{"name":"${ t . testName } "}` } ,
118147 argv : [ 'npm' ] ,
119148 global : true ,
120149 } )
121150 t . equal ( wroteFile , false )
122151 t . equal ( result , null )
123- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
124152 } )
125153
126154 t . test ( 'do not suggest update if already updating with spec' , async t => {
127- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , {
155+ const { wroteFile, result } = await runUpdateNotifier ( t , {
156+ PACOTE_MOCK_REQ_COUNT : 0 ,
128157 command : 'install' ,
129158 prefixDir : { 'package.json' : `{"name":"${ t . testName } "}` } ,
130159 argv : [ 'npm@latest' ] ,
131160 global : true ,
132161 } )
133162 t . equal ( wroteFile , false )
134163 t . equal ( result , null )
135- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
136164 } )
137165
138166 t . test ( 'do not update if same as latest' , async t => {
139- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t )
167+ const { wroteFile, result } = await runUpdateNotifier ( t )
140168 t . equal ( wroteFile , true )
141169 t . equal ( result , null )
142- t . strictSame ( MANIFEST_REQUEST , [ 'npm@latest' ] , 'requested latest version' )
143170 } )
144171 t . test ( 'check if stat errors (here for coverage)' , async t => {
145172 const STAT_ERROR = new Error ( 'blorg' )
146- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , { STAT_ERROR } )
173+ const { wroteFile, result } = await runUpdateNotifier ( t , { STAT_ERROR } )
147174 t . equal ( wroteFile , true )
148175 t . equal ( result , null )
149- t . strictSame ( MANIFEST_REQUEST , [ 'npm@latest' ] , 'requested latest version' )
150176 } )
151177 t . test ( 'ok if write errors (here for coverage)' , async t => {
152178 const WRITE_ERROR = new Error ( 'grolb' )
153- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , { WRITE_ERROR } )
179+ const { wroteFile, result } = await runUpdateNotifier ( t , { WRITE_ERROR } )
154180 t . equal ( wroteFile , true )
155181 t . equal ( result , null )
156- t . strictSame ( MANIFEST_REQUEST , [ 'npm@latest' ] , 'requested latest version' )
157182 } )
158183 t . test ( 'ignore pacote failures (here for coverage)' , async t => {
159184 const PACOTE_ERROR = new Error ( 'pah-KO-tchay' )
160- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , { PACOTE_ERROR } )
185+ const { wroteFile, result } = await runUpdateNotifier ( t , {
186+ PACOTE_ERROR , PACOTE_MOCK_REQ_COUNT : 0 ,
187+ } )
161188 t . equal ( result , null )
162189 t . equal ( wroteFile , true )
163- t . strictSame ( MANIFEST_REQUEST , [ 'npm@latest' ] , 'requested latest version' )
164190 } )
165191 t . test ( 'do not update if newer than latest, but same as next' , async t => {
166192 const {
167193 wroteFile,
168194 result,
169- MANIFEST_REQUEST ,
170195 } = await runUpdateNotifier ( t , { version : NEXT_VERSION } )
171196 t . equal ( result , null )
172197 t . equal ( wroteFile , true )
173- const reqs = [ 'npm@latest' , `npm@^${ NEXT_VERSION } ` ]
174- t . strictSame ( MANIFEST_REQUEST , reqs , 'requested latest and next versions' )
175198 } )
176199 t . test ( 'do not update if on the latest beta' , async t => {
177200 const {
178201 wroteFile,
179202 result,
180- MANIFEST_REQUEST ,
181203 } = await runUpdateNotifier ( t , { version : CURRENT_BETA } )
182204 t . equal ( result , null )
183205 t . equal ( wroteFile , true )
184- const reqs = [ `npm@^${ CURRENT_BETA } ` ]
185- t . strictSame ( MANIFEST_REQUEST , reqs , 'requested latest and next versions' )
186206 } )
187207
188208 t . test ( 'do not update in CI' , async t => {
189- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , { mocks : {
209+ const { wroteFile, result } = await runUpdateNotifier ( t , { mocks : {
190210 'ci-info' : { isCI : true , name : 'something' } ,
191- } } )
211+ } ,
212+ PACOTE_MOCK_REQ_COUNT : 0 } )
192213 t . equal ( wroteFile , false )
193214 t . equal ( result , null )
194- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
195215 } )
196216
197217 t . test ( 'only check weekly for GA releases' , async t => {
198218 // One week (plus five minutes to account for test environment fuzziness)
199219 const STAT_MTIME = Date . now ( ) - 1000 * 60 * 60 * 24 * 7 + 1000 * 60 * 5
200- const { wroteFile, result, MANIFEST_REQUEST } = await runUpdateNotifier ( t , { STAT_MTIME } )
220+ const { wroteFile, result } = await runUpdateNotifier ( t , {
221+ STAT_MTIME ,
222+ PACOTE_MOCK_REQ_COUNT : 0 ,
223+ } )
201224 t . equal ( wroteFile , false , 'duration was not reset' )
202225 t . equal ( result , null )
203- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
204226 } )
205227
206228 t . test ( 'only check daily for betas' , async t => {
@@ -209,37 +231,48 @@ t.test('situations in which we do not notify', t => {
209231 const {
210232 wroteFile,
211233 result,
212- MANIFEST_REQUEST ,
213- } = await runUpdateNotifier ( t , { STAT_MTIME , version : HAVE_BETA } )
234+ } = await runUpdateNotifier ( t , { STAT_MTIME , version : HAVE_BETA , PACOTE_MOCK_REQ_COUNT : 0 } )
214235 t . equal ( wroteFile , false , 'duration was not reset' )
215236 t . equal ( result , null )
216- t . strictSame ( MANIFEST_REQUEST , [ ] , 'no requests for manifests' )
217237 } )
218238
219239 t . end ( )
220240} )
221241
242+ t . test ( 'notification situation with engine compatibility' , async t => {
243+ // no version which are greater than node 1.0.0 should be selected.
244+ mockGlobals ( t , { 'process.version' : 'v1.0.0' } , { replace : true } )
245+
246+ const {
247+ wroteFile,
248+ result,
249+ } = await runUpdateNotifier ( t , {
250+ version : NEXT_VERSION_ENGINE_COMPATIBLE_MINOR ,
251+ PACOTE_MOCK_REQ_COUNT : 1 } )
252+
253+ t . matchSnapshot ( result )
254+ t . equal ( wroteFile , true )
255+ } )
256+
222257t . test ( 'notification situations' , async t => {
223258 const cases = {
224- [ HAVE_BETA ] : [ `^{V}` ] ,
225- [ NEXT_PATCH ] : [ `latest` , `^{V}` ] ,
226- [ NEXT_MINOR ] : [ `latest` , `^{V}` ] ,
227- [ CURRENT_PATCH ] : [ 'latest' ] ,
228- [ CURRENT_MINOR ] : [ 'latest' ] ,
229- [ CURRENT_MAJOR ] : [ 'latest' ] ,
259+ [ HAVE_BETA ] : 1 ,
260+ [ NEXT_PATCH ] : 2 ,
261+ [ NEXT_MINOR ] : 2 ,
262+ [ CURRENT_PATCH ] : 1 ,
263+ [ CURRENT_MINOR ] : 1 ,
264+ [ CURRENT_MAJOR ] : 1 ,
230265 }
231266
232- for ( const [ version , reqs ] of Object . entries ( cases ) ) {
267+ for ( const [ version , requestCount ] of Object . entries ( cases ) ) {
233268 for ( const color of [ false , 'always' ] ) {
234269 await t . test ( `${ version } - color=${ color } ` , async t => {
235270 const {
236271 wroteFile,
237272 result,
238- MANIFEST_REQUEST ,
239- } = await runUpdateNotifier ( t , { version, color } )
273+ } = await runUpdateNotifier ( t , { version, color, PACOTE_MOCK_REQ_COUNT : requestCount } )
240274 t . matchSnapshot ( result )
241275 t . equal ( wroteFile , true )
242- t . strictSame ( MANIFEST_REQUEST , reqs . map ( r => `npm@${ r . replace ( '{V}' , version ) } ` ) )
243276 } )
244277 }
245278 }
0 commit comments