1- import { Container , ContainerModule } from '@theia/core/shared/inversify' ;
1+ import {
2+ Container ,
3+ ContainerModule ,
4+ injectable ,
5+ } from '@theia/core/shared/inversify' ;
26import { assert , expect } from 'chai' ;
37import fetch from 'cross-fetch' ;
48import { posix } from 'path' ;
@@ -19,13 +23,14 @@ import queryString = require('query-string');
1923const timeout = 60 * 1_000 ;
2024
2125describe ( 'create-api' , ( ) => {
22- let createApi : CreateApi ;
26+ let createApi : TestCreateApi ;
2327
2428 before ( async function ( ) {
2529 this . timeout ( timeout ) ;
2630 try {
2731 const accessToken = await login ( ) ;
28- createApi = createContainer ( accessToken ) . get < CreateApi > ( CreateApi ) ;
32+ createApi =
33+ createContainer ( accessToken ) . get < TestCreateApi > ( TestCreateApi ) ;
2934 } catch ( err ) {
3035 if ( err instanceof LoginFailed ) {
3136 return this . skip ( ) ;
@@ -43,7 +48,7 @@ describe('create-api', () => {
4348 const container = new Container ( { defaultScope : 'Singleton' } ) ;
4449 container . load (
4550 new ContainerModule ( ( bind ) => {
46- bind ( CreateApi ) . toSelf ( ) . inSingletonScope ( ) ;
51+ bind ( TestCreateApi ) . toSelf ( ) . inSingletonScope ( ) ;
4752 bind ( SketchCache ) . toSelf ( ) . inSingletonScope ( ) ;
4853 bind ( AuthenticationClientService ) . toConstantValue ( <
4954 AuthenticationClientService
@@ -224,6 +229,47 @@ describe('create-api', () => {
224229 expect ( findByName ( otherName , sketches ) ) . to . be . not . undefined ;
225230 } ) ;
226231
232+ [
233+ [ - 1 , 1 ] ,
234+ [ 0 , 2 ] ,
235+ [ 1 , 2 ] ,
236+ ] . forEach ( ( [ diff , expected ] ) =>
237+ it ( `should not run unnecessary fetches when retrieving all sketches (sketch count ${
238+ diff < 0 ? '<' : diff > 0 ? '>' : '='
239+ } limit)`, async ( ) => {
240+ const content = 'void setup(){} void loop(){}' ;
241+ const maxLimit = 50 ; // https://github.com/arduino/arduino-ide/pull/875
242+ const sketchCount = maxLimit + diff ;
243+ const sketchNames = [ ...Array ( sketchCount ) . keys ( ) ] . map ( ( ) => v4 ( ) ) ;
244+
245+ await sketchNames
246+ . map ( ( name ) => createApi . createSketch ( toPosix ( name ) , content ) )
247+ . reduce ( async ( acc , curr ) => {
248+ await acc ;
249+ return curr ;
250+ } , Promise . resolve ( ) as Promise < unknown > ) ;
251+
252+ createApi . resetRequestRecording ( ) ;
253+ const sketches = await createApi . sketches ( ) ;
254+ const allRequests = createApi . requestRecording . slice ( ) ;
255+
256+ expect ( sketches . length ) . to . be . equal ( sketchCount ) ;
257+ sketchNames . forEach (
258+ ( name ) => expect ( findByName ( name , sketches ) ) . to . be . not . undefined
259+ ) ;
260+
261+ expect ( allRequests . length ) . to . be . equal ( expected ) ;
262+ const getSketchesRequests = allRequests . filter (
263+ ( description ) =>
264+ description . method === 'GET' &&
265+ description . pathname === '/create/v2/sketches' &&
266+ description . query &&
267+ description . query . includes ( `limit=${ maxLimit } ` )
268+ ) ;
269+ expect ( getSketchesRequests . length ) . to . be . equal ( expected ) ;
270+ } )
271+ ) ;
272+
227273 [ '.' , '-' , '_' ] . map ( ( char ) => {
228274 it ( `should create a new sketch with '${ char } ' in the sketch folder name although it's disallowed from the Create Editor` , async ( ) => {
229275 const name = `sketch${ char } ` ;
@@ -332,3 +378,44 @@ class LoginFailed extends Error {
332378 Object . setPrototypeOf ( this , LoginFailed . prototype ) ;
333379 }
334380}
381+
382+ @injectable ( )
383+ class TestCreateApi extends CreateApi {
384+ private _recording : RequestDescription [ ] = [ ] ;
385+
386+ constructor ( ) {
387+ super ( ) ;
388+ const originalRun = this [ 'run' ] ;
389+ this [ 'run' ] = ( url , init , resultProvider ) => {
390+ this . _recording . push ( createRequestDescription ( url , init ) ) ;
391+ return originalRun . bind ( this ) ( url , init , resultProvider ) ;
392+ } ;
393+ }
394+
395+ resetRequestRecording ( ) : void {
396+ this . _recording = [ ] ;
397+ }
398+
399+ get requestRecording ( ) : RequestDescription [ ] {
400+ return this . _recording ;
401+ }
402+ }
403+
404+ interface RequestDescription {
405+ readonly origin : string ;
406+ readonly pathname : string ;
407+ readonly query ?: string ;
408+
409+ readonly method ?: string | undefined ;
410+ readonly serializedBody ?: string | undefined ;
411+ }
412+
413+ function createRequestDescription (
414+ url : URL ,
415+ init ?: RequestInit | undefined
416+ ) : RequestDescription {
417+ const { origin, pathname, search : query } = url ;
418+ const method = init ?. method ;
419+ const serializedBody = init ?. body ?. toString ( ) ;
420+ return { origin, pathname, query, method, serializedBody } ;
421+ }
0 commit comments