import * as assert from 'assert' ; import * as fse from 'fs-extra' ; import * as path from 'path' ; import { Disposable } from 'vscode' ; import { runWithTestActionContext , TestActionContext } from 'vscode-azureextensiondev' ; import { createFunctionInternal , FuncVersion , getRandomHexString , IFunctionTemplate , ProjectLanguage , TemplateFilter , TemplateSource } from '../../extension.bundle' ; import { addParallelSuite , ParallelSuiteOptions , ParallelTest } from '../addParallelSuite' ; import { runForTemplateSource , testFolderPath } from '../global.test' ; export interface CreateFunctionTestCase { functionName : string ; inputs : string [ ] ; skip ? : boolean ; } export abstract class FunctionTesterBase implements Disposable { public projectPath : string ; public readonly version : FuncVersion ; public abstract language : ProjectLanguage ; private readonly testedFunctions : string [ ] = [ ] ; private _source : TemplateSource ; public constructor ( version , source ) { this . version = version ; this . _source = source ; this . projectPath = path . join ( testFolderPath , getRandomHexString ( ) ) ; } public get suiteName ( ) : string { return `Create Function ${ this . language } ${ this . version } (${ this . _source })` ; } /**
     * NOTE: The first entry in the returned array is used for validating contents
     */ public abstract getExpectedPaths ( functionName ) ; public async initAsync ( ) { await runWithTestActionContext ( 'testCreateFunctionInit' , async ( context ) => { await runForTemplateSource ( context , this . _source , async ( templateProvider ) => { await this . initializeTestFolder ( this . projectPath ) ; await templateProvider . getFunctionTemplates ( context , this . projectPath , this . language , this . version , TemplateFilter . Verified , undefined ) ; } ) ; } ) ; } public async dispose ( ) { await runWithTestActionContext ( 'testCreateFunctionDispose' , async ( context ) => { await runForTemplateSource ( context , this . _source , async ( templateProvider ) => { const templates = await templateProvider . getFunctionTemplates ( context , this . projectPath , this . language , this . version , TemplateFilter . Verified , undefined ) ; assert . deepEqual ( this . testedFunctions . sort ( ) , templates . map ( t => t . name ) . sort ( ) , 'Not all "Verified" templates were tested' ) ; } ) ; } ) ; } public addParallelSuite ( testCases , options ? ) { const parallelTests = [ ] ; for ( const testCase of testCases ) { parallelTests . push ( { title : testCase . functionName , skip : testCase . skip , callback : async ( ) => { await this . testCreateFunction ( testCase . functionName , ... testCase . inputs ) ; } } ) ; } addParallelSuite ( parallelTests , { title : this . suiteName , suiteSetup : async ( ) => { await this . initAsync ( ) ; } , suiteTeardown : async ( ) => { await this . dispose ( ) ; } , ... options } ) ; } public async testCreateFunction ( templateName , ... inputs ) { this . testedFunctions . push ( templateName ) ; await runWithTestActionContext ( 'testCreateFunction' , async ( context ) => { await runForTemplateSource ( context , this . _source , async ( ) => { await this . testCreateFunctionInternal ( context , this . projectPath , templateName , inputs . slice ( ) ) ; } ) ; } ) ; } public async validateFunction ( testFolder , funcName , expectedContents ) { const expectedPaths = this . getExpectedPaths ( funcName ) ; for ( const expectedPath of expectedPaths ) { const filePath = path . join ( testFolder , expectedPath ) ; assert . ok ( await fse . pathExists ( filePath ) , `Failed to find expected path "${ expectedPath }"` ) ; } const mainFileName = expectedPaths [ 0 ] ; const mainFilePath = path . join ( testFolder , mainFileName ) ; const contents = ( await fse . readFile ( mainFilePath ) ) . toString ( ) ; for ( const expectedContent of expectedContents ) { assert . ok ( contents . includes ( expectedContent ) || contents . includes ( expectedContent . toLowerCase ( ) ) , `Failed to find expected content "${ expectedContent }" in "${ mainFileName }"` ) ; } } protected async initializeTestFolder ( testFolder ) { await fse . ensureDir ( path . join ( testFolder , '.vscode' ) ) ; await Promise . all ( [ fse . writeFile ( path . join ( testFolder , 'host.json' ) , '{}' ) , fse . writeFile ( path . join ( testFolder , 'local.settings.json' ) , '{ "Values": { "AzureWebJobsStorage": "test" } }' ) , fse . writeFile ( path . join ( testFolder , '.vscode' , 'launch.json' ) , '' ) ] ) ; } private async testCreateFunctionInternal ( context , testFolder , templateName , inputs ) { const expectedContents = inputs . slice ( 0 ) ; const funcName = templateName . replace ( /[^a-z0-9]/ig , '' ) + getRandomHexString ( ) ; inputs . unshift ( funcName ) ; inputs . unshift ( templateName ) ; await context . ui . runWithInputs ( inputs , async ( ) => { await createFunctionInternal ( context , { folderPath : testFolder , language : < any > this . language , version : this . version } ) ; } ) ; await this . validateFunction ( testFolder , funcName , expectedContents ) ; } }