import * as fs from 'fs' ; import { IConfig } from '../src/config-loader' ; export enum SectionType { unknown , json , bash } export interface ISectionTest { title : string ; type : SectionType ; config : IConfig ; result : string [ ] [ ] ; commands : string [ ] ; error ? : string ; } export class MarkdownParser { private readonly sections : Map < string , string [ ] > ; public constructor ( fileName , exclude = [ ] ) { const buffer = fs . readFileSync ( fileName ) ; const fileContent = buffer . toString ( ) . split ( '\n' ) ; this . sections = new Map ( ) ; try { const sections = MarkdownParser . getSections ( fileContent , '^(###|##) (.*)' ) ; for ( const [ key , value ] of sections ) { if ( exclude . includes ( key ) ) continue ; if ( value . length !== 1 ) throw new Error ( 'Invalid markdown section: ' + key ) ; this . sections . set ( key , value [ 0 ] ) ; } } catch ( error ) { console . error ( 'Error loading "' + fileName + '" markdown file ' , error . message ) ; } } public getSectionTests ( ) { const result = [ ] ; for ( const [ title , content ] of this . sections ) { const commands = MarkdownParser . getCommands ( content , '^\\*\\*Run\\*\\*\\: ' ) ; const sections = MarkdownParser . getSections ( content , '^\\s*```(.*)' ) ; result . push ( ... MarkdownParser . parseSectionJSONTests ( sections , title , commands ) ) ; result . push ( ... MarkdownParser . parseSectionBashTests ( sections , title , commands ) ) ; } return result ; } private static parseSectionJSONTests ( sections , title , commands ) { const result = [ ] ; const content = sections . get ( 'json' ) ; const output = sections . get ( 'text' ) || [ ] ; if ( content ) { let config = { } as any ; let sectionError = null ; for ( const item of content ) { try { config = JSON . parse ( item ? item . join ( '\n' ) : '{}' ) ; } catch ( error ) { sectionError = 'Unable to load markdown example: ' + error . message ; } let outputResult = null ; if ( output [ result . length ] ) { outputResult = output . map ( item => item . map ( item => item . replace ( /\r$/ , '' ) ) ) ; } result . push ( { title : title , config : config , result : outputResult , type : SectionType . json , commands : commands , error : sectionError } ) ; } } return result ; } private static parseSectionBashTests ( sections , title , commands ) { const result = [ ] ; const content = sections . get ( 'bash' ) ; let index = 0 ; if ( content ) { for ( const item of content ) { result . push ( { title : title , config : null , result : [ item . map ( row => row . replace ( '\r' , '' ) ) ] , type : SectionType . bash , commands : [ commands [ index ++ ] ] , error : null } ) ; } } return result ; } private static getCommands ( content , pattern ) { const result = [ ] ; const expression = new RegExp ( pattern ) ; for ( const line of content ) { if ( line . match ( expression ) !== null ) { const expression = /`(.*?)`/g ; let match ; while ( ( match = expression . exec ( line ) ) != null ) { result . push ( match [ 1 ] ) ; } } } return result ; } private static getSections ( content , pattern ) { const result = new Map ( ) ; const expression = new RegExp ( pattern ) ; let block = [ ] ; for ( const line of content ) { const matches = line . match ( expression ) ; if ( matches !== null ) { const key = matches [ matches . length - 1 ] . trim ( ) ; block = [ ] ; let section = result . get ( key ) ; if ( ! section ) { section = [ ] ; result . set ( key , section ) ; } section . push ( block ) ; } else { block . push ( line ) ; } } return result ; } }