import * as assert from "assert" ; import { Uri } from "vscode" ; import { parseError } from "vscode-azureextensionui" ; import { assertNever , LinkedFileLoadState , notifications , notifyTemplateGraphAvailable } from "../../extension.bundle" ; import { ExpectedDiagnostics , IExpectedDiagnostic , simplifyBadTypeResourceMessage , testDiagnostics , testDiagnosticsFromUri } from "../support/diagnostics" ; import { ensureLanguageServerAvailable } from "../support/ensureLanguageServerAvailable" ; import { resolveInTestFolder } from "../support/resolveInTestFolder" ; import { writeToLog } from "../support/testLog" ; import { testWithLanguageServerAndRealFunctionMetadata } from "../support/testWithLanguageServer" ; import { isWin32 } from "../testConstants" ; suite ( "Linked templates functional tests" , ( ) => { function tcString ( s , testCase ) { return s . replace ( /<TC>/g , testCase ) ; } function tcDiagnostics ( ed , testCase ) { if ( ed . length === 0 ) { return [ ] ; } else if ( typeof ed [ 0 ] === 'string' ) { return ( < string [ ] > ed ) . map ( ( s ) => tcString ( < string > s , testCase ) ) ; } else { return ( < IExpectedDiagnostic [ ] > ed ) . map ( ( d ) => { const d2 = Object . assign ( { } , d , { message : tcString ( d . message , testCase ) } ) ; return d2 ; } ) ; } } async function waitForGraphAvailable ( mainTemplate , childTemplate ) { await new Promise < void > ( ( resolve , reject ) : void => { try { const disposable = notifyTemplateGraphAvailable ( e => { if ( Uri . parse ( e . rootTemplateUri ) . fsPath === mainTemplate ) { writeToLog ( `Graph available notification for ${ mainTemplate }... Looking for child in the graph: ${ childTemplate }` ) ; const child = e . linkedTemplates . find ( lt => Uri . parse ( lt . fullUri ) . fsPath === childTemplate ) ; let ready ; if ( child ) { switch ( child . loadState ) { case LinkedFileLoadState . Loading : case LinkedFileLoadState . NotLoaded : writeToLog ( `...${ child . originalPath }: load state = ${ child . loadState }, therefore not ready yet` ) ; ready = false ; break ; case LinkedFileLoadState . LoadFailed : case LinkedFileLoadState . NotSupported : case LinkedFileLoadState . SuccessfullyLoaded : case LinkedFileLoadState . TooDeep : writeToLog ( `...${ child . originalPath }: load state = ${ child . loadState } => READY` ) ; ready = true ; break ; default : assertNever ( child . loadState ) ; } } else { ready = false ; writeToLog ( `... child not found in graph yet` ) ; } if ( ready ) { writeToLog ( `...READY` ) ; disposable . dispose ( ) ; resolve ( ) ; } else { writeToLog ( `... not ready yet` ) ; } } } ) ; } catch ( err ) { reject ( err ) ; } } ) ; } function createLinkedTemplateTest ( testCase , testDescription , options ) { testWithLanguageServerAndRealFunctionMetadata ( `${ testCase }/${ testDescription }` , async ( ) => { const mainTemplatePath = resolveInTestFolder ( tcString ( options . mainTemplateFile , testCase ) ) ; assert ( mainTemplatePath ) ; const client = await ensureLanguageServerAvailable ( ) ; client . onNotification ( notifications . Diagnostics . codeAnalysisStarting , async ( args ) => { } ) ; let waitForChildPromises = [ ] ; for ( const expectedLinkedTemplate of options . linkedTemplates ) { const childPath = resolveInTestFolder ( tcString ( expectedLinkedTemplate . linkedTemplateFile , testCase ) ) ; const parentPath = resolveInTestFolder ( tcString ( expectedLinkedTemplate . parentTemplateFile , testCase ) ) ; waitForChildPromises . push ( waitForGraphAvailable ( parentPath , childPath ) ) ; } const waitAllForChildPromises = Promise . all ( waitForChildPromises ) ; writeToLog ( "Testing diagnostics in main template." ) ; await testDiagnostics ( mainTemplatePath , { parametersFile : options . mainParametersFile ? tcString ( options . mainParametersFile , testCase ) : undefined , waitForDiagnosticsFilter : async ( results ) : Promise < boolean > => { await waitAllForChildPromises ; if ( options . waitForDiagnosticSubstring ) { return results . diagnostics . some ( d => d . message . includes ( options . waitForDiagnosticSubstring ! ) ) ; } return true ; } , transformResults : simplifyBadTypeResourceMessage } , tcDiagnostics ( options . mainTemplateExpected , testCase ) ) ; writeToLog ( "Diagnostics in main template were correct." ) ; for ( const linkedTemplate of options . linkedTemplates ) { const childPath = resolveInTestFolder ( tcString ( linkedTemplate . linkedTemplateFile , testCase ) ) ; const childUri = Uri . file ( childPath ) ; try { writeToLog ( `Testing diagnostics in ${ linkedTemplate . linkedTemplateFile }` ) ; await testDiagnosticsFromUri ( childUri , { waitForDiagnosticsFilter : ( results ) : boolean => { if ( linkedTemplate . waitForDiagnosticSubstring ) { return results . diagnostics . some ( d => d . message . includes ( linkedTemplate . waitForDiagnosticSubstring ! ) ) ; } return true ; } , transformResults : simplifyBadTypeResourceMessage } , tcDiagnostics ( linkedTemplate . expected , testCase ) ) ; } catch ( err ) { throw new Error ( `Diagnostics did not match expected for linked template ${ childPath }: ${ parseError ( err ) . message }` ) ; } } } ) ; } if ( ! isWin32 ) { createLinkedTemplateTest ( "relative-notfound" , "expecting error: child not found" , { mainTemplateFile : "templates/linkedTemplates/<TC>/<TC>.json" , mainParametersFile : "<TC>.parameters.json" , waitForDiagnosticSubstring : 'not found' , mainTemplateExpected : [ `Error: Template validation failed: Linked template file not found: ` + `"${ resolveInTestFolder ( 'templates/linkedTemplates/<TC>/subfolder/child.json' ) }"` + ` (arm-template (validation)) [12,27]` ] , linkedTemplates : [ ] } ) ; } suite ( "Parameter validation" , ( ) => { } ) ; suite ( "uri" , ( ) => { } ) ; } ) ;