import { expect } from "chai" ; import { dir as tempDir , file as tempFile } from "tmp-promise" ; import { readdir , mkdirs , writeFile } from "fs-extra" ; import { sync as rimrafSync } from "rimraf" ; import { getSemanticModel , getSemanticModelWithFetcher , getCacheFilePath , getCacheFolder , } from "../src/ui5-model" ; import { UI5SemanticModel } from "@ui5-language-assistant/semantic-model-types" ; import { FetchResponse } from "../api" ; import { expectExists } from "@ui5-language-assistant/test-utils" ; import { forEach , isPlainObject } from "lodash" ; describe ( "the UI5 language assistant ui5 model" , ( ) => { const GET_MODEL_TIMEOUT = 10000 ; const VERSION = "1.71.14" ; const NO_CACHE_FOLDER = undefined ; function assertSemanticModel ( ui5Model ) { expect ( ui5Model . version ) . to . equal ( VERSION ) ; expect ( Object . keys ( ui5Model . classes ) . length ) . to . be . greaterThan ( 200 ) ; expect ( Object . keys ( ui5Model . namespaces ) . length ) . to . be . greaterThan ( 200 ) ; expect ( Object . keys ( ui5Model . interfaces ) . length ) . to . be . greaterThan ( 30 ) ; expect ( Object . keys ( ui5Model . functions ) . length ) . to . be . greaterThan ( 30 ) ; expect ( Object . keys ( ui5Model . enums ) . length ) . to . be . greaterThan ( 200 ) ; expect ( Object . keys ( ui5Model . typedefs ) . length ) . to . be . greaterThan ( 10 ) ; expect ( Object . keys ( ui5Model . classes ) ) . to . include ( "sap.m.List" ) ; expect ( Object . keys ( ui5Model . namespaces ) ) . to . include ( "sap.m" ) ; expect ( Object . keys ( ui5Model . interfaces ) ) . to . include ( "sap.f.ICard" ) ; expect ( Object . keys ( ui5Model . functions ) ) . to . include ( "module:sap/base/assert" ) ; expect ( Object . keys ( ui5Model . enums ) ) . to . include ( "sap.m.ButtonType" ) ; expect ( Object . keys ( ui5Model . typedefs ) ) . to . include ( "sap.ui.fl.Selector" ) ; expect ( Object . keys ( ui5Model . classes ) ) . to . include ( "sap.ui.vk.Camera" ) ; expect ( Object . keys ( ui5Model . namespaces ) ) . to . include ( "sap.apf.base" ) ; expect ( Object . keys ( ui5Model . enums ) ) . to . include ( "sap.ca.ui.charts.ChartSelectionMode" ) ; } it ( "will get UI5 semantic model" , async ( ) => { const ui5Model = await getSemanticModel ( NO_CACHE_FOLDER ) ; assertSemanticModel ( ui5Model ) ; } ) . timeout ( GET_MODEL_TIMEOUT ) ; it ( "doesn't fail if a file cannot be fetched" , async ( ) => { const ui5Model = await getSemanticModelWithFetcher ( async ( url ) => { return { ok : false , json : ( ) : never => { throw new Error ( `Cannot read from ${ url }` ) ; } , } ; } , NO_CACHE_FOLDER ) ; expect ( ui5Model ) . to . exist ; } ) ; describe ( "with cache" , ( ) => { describe ( "cache in temp dir" , ( ) => { let cachePath ; beforeEach ( async ( ) => { ( { path : cachePath } = await tempDir ( ) ) ; } ) ; afterEach ( async ( ) => { rimrafSync ( cachePath ) ; } ) ; it ( "caches the model the first time getSemanticModel is called" , async ( ) => { const ui5Model = await getSemanticModel ( cachePath ) ; assertSemanticModel ( ui5Model ) ; const files = await readdir ( cachePath ) ; expect ( files ) . to . not . be . empty ; let fetcherCalled = false ; const ui5ModelFromCache = await getSemanticModelWithFetcher ( ( url ) : never => { fetcherCalled = true ; throw new Error ( `The files should be taken from the cache, got call for ${ url }` ) ; } , cachePath ) ; expect ( fetcherCalled ) . to . be . false ; expect ( ui5ModelFromCache ) . to . not . equal ( ui5Model ) ; forEach ( ui5Model , ( value , key ) => { if ( isPlainObject ( value ) ) { expect ( Object . keys ( ui5ModelFromCache [ key as keyof UI5SemanticModel ] as Record < string , unknown > ) ) . to . deep . equalInAnyOrder ( Object . keys ( value as Record < string , unknown > ) ) ; } } ) ; assertSemanticModel ( ui5ModelFromCache ) ; } ) . timeout ( GET_MODEL_TIMEOUT ) ; it ( "doesn't fail when file cannot be written to the cache" , async ( ) => { const cacheFilePath = getCacheFilePath ( getCacheFolder ( cachePath , VERSION ) , "sap.m" ) ; expectExists ( cacheFilePath , "cacheFilePath" ) ; await mkdirs ( cacheFilePath ) ; const ui5Model = await getSemanticModel ( cachePath ) ; expect ( ui5Model ) . to . exist ; expect ( Object . keys ( ui5Model . namespaces ) ) . to . contain ( "sap.m" ) ; expect ( ui5Model . namespaces [ "sap.m" ] . library ) . to . equal ( "sap.m" ) ; } ) . timeout ( GET_MODEL_TIMEOUT ) ; it ( "doesn't fail when file cannot be read from the cache" , async ( ) => { const cacheFolder = getCacheFolder ( cachePath , VERSION ) ; await mkdirs ( cacheFolder ) ; const cacheFilePath = getCacheFilePath ( cacheFolder , "sap.m" ) ; expectExists ( cacheFilePath , "cacheFilePath" ) ; await writeFile ( cacheFilePath , "not json" ) ; const ui5Model = await getSemanticModel ( cachePath ) ; expect ( ui5Model ) . to . exist ; expect ( Object . keys ( ui5Model . namespaces ) ) . to . contain ( "sap.m" ) ; expect ( ui5Model . namespaces [ "sap.m" ] . library ) . to . equal ( "sap.m" ) ; } ) . timeout ( GET_MODEL_TIMEOUT ) ; } ) ; describe ( "cache path is a file" , async ( ) => { let cachePath ; let cleanup ; beforeEach ( async ( ) => { ( { path : cachePath , cleanup } = await tempFile ( ) ) ; } ) ; afterEach ( async ( ) => { await cleanup ( ) ; } ) ; it ( "does not cache the model" , async ( ) => { const ui5Model = await getSemanticModel ( cachePath ) ; assertSemanticModel ( ui5Model ) ; let fetcherCalled = false ; await getSemanticModelWithFetcher ( async ( ) : Promise < FetchResponse > => { fetcherCalled = true ; return { ok : true , json : async ( ) : Promise < unknown > => { return { } ; } , } ; } , cachePath ) ; expect ( fetcherCalled ) . to . be . true ; } ) . timeout ( GET_MODEL_TIMEOUT ) ; } ) ; } ) ; } ) ;