import * as _ from "lodash" ; import * as path from "path" ; import * as fs from "fs" ; import { randomBytes } from "crypto" ; import * as shell from "shelljs" ; const Ajv = require ( "ajv" ) ; import { compareJsonFileToJson , debug , exec , execAsync , failWith , inDir , quicktype , quicktypeForLanguage , Sample , samplesFromSources , testsInDir , ComparisonArgs , mkdirs , callAndExpectFailure , } from "./utils" ; import * as languages from "./languages" ; import { RendererOptions } from "../dist/quicktype-core/Run" ; import { mustNotHappen , defined } from "../dist/quicktype-core/support/Support" ; import { DefaultDateTimeRecognizer } from "../dist/quicktype-core/DateTime" ; const chalk = require ( "chalk" ) ; const timeout = require ( "promise-timeout" ) . timeout ; const OUTPUT_DIR = process . env . OUTPUT_DIR ; const ONLY_OUTPUT = process . env . ONLY_OUTPUT !== undefined ; const MAX_TEST_RUNTIME_MS = 30 * 60 * 1000 ; /**
 * These are tests where we have stringified integers that might be serialized
 * back as integers, which happens in heterogenous arrays such as ["123", 456].
 */ const testsWithStringifiedIntegers = [ "nst-test-suite.json" , "kitchen-sink.json" ] ; function allowStringifiedIntegers ( language , test ) { if ( language . features . indexOf ( "integer-string" ) < 0 ) return false ; return testsWithStringifiedIntegers . indexOf ( path . basename ( test ) ) >= 0 ; } function pathWithoutExtension ( fullPath , extension ) { return path . join ( path . dirname ( fullPath ) , path . basename ( fullPath , extension ) ) ; } function additionalTestFiles ( base , extension , features = [ ] ) { const additionalFiles = [ ] ; function tryAdd ( filename ) { if ( ! fs . existsSync ( filename ) ) return false ; additionalFiles . push ( filename ) ; return true ; } let fn = `${ base }.${ extension }` ; tryAdd ( fn ) ; let i = 1 ; let found ; do { found = false ; fn = `${ base }.${ i . toString ( ) }.${ extension }` ; found = tryAdd ( fn ) || found ; for ( const feature of features ) { found = tryAdd ( `${ base }.${ i . toString ( ) }.fail.${ feature }.${ extension }` ) || found ; } found = tryAdd ( `${ base }.${ i . toString ( ) }.fail.${ extension }` ) || found ; i ++ ; } while ( found ) ; return additionalFiles ; } function runEnvForLanguage ( additionalRendererOptions ) { const newEnv = Object . assign ( { } , process . env ) ; for ( const o of Object . getOwnPropertyNames ( additionalRendererOptions ) ) { newEnv [ "QUICKTYPE_" + o . toUpperCase ( ) . replace ( "-" , "_" ) ] = additionalRendererOptions [ o ] ; } return newEnv ; } function comparisonArgs ( language , inputFilename , expectedFilename , additionalRendererOptions ) { return { expectedFile : expectedFilename , given : { command : defined ( language . runCommand ) ( inputFilename ) , env : runEnvForLanguage ( additionalRendererOptions ) , } , strict : false , allowMissingNull : language . allowMissingNull , allowStringifiedIntegers : allowStringifiedIntegers ( language , expectedFilename ) , } ; } const timeMap = new Map < string , number > ( ) ; function timeStart ( message ) { timeMap . set ( message , Date . now ( ) ) ; } function timeEnd ( message , suffix ) { const start = timeMap . get ( message ) ; const fullMessage = message + suffix ; if ( start === undefined ) { console . log ( fullMessage + ": " + chalk . red ( "UNKNOWN TIMING" ) ) ; return ; } const diff = Date . now ( ) - start ; console . log ( fullMessage + `: ${ diff } ms` ) ; } export abstract class Fixture { abstract name : string ; constructor ( public language ) { } runForName ( name ) { return this . name === name ; } async setup ( ) { return ; } abstract getSamples ( sources ) ; abstract runWithSample ( sample , index , total ) ; getRunDirectory ( ) { return `test/runs/${ this . name }-${ randomBytes ( 3 ) . toString ( "hex" ) }` ; } runMessageStart ( sample , index , total , cwd , shouldSkip ) { const rendererOptions = _ . map ( sample . additionalRendererOptions , ( v , k ) => `${ k }: ${ v }` ) . join ( ", " ) ; const messageParts = [ `*` , chalk . dim ( `[${ index + 1 }/${ total }]` ) , chalk . magenta ( this . name ) + chalk . dim ( `(${ rendererOptions })` ) , path . join ( cwd , chalk . cyan ( path . basename ( sample . path ) ) ) , ] ; if ( shouldSkip ) { messageParts . push ( chalk . red ( "SKIP" ) ) ; } const message = messageParts . join ( " " ) ; timeStart ( message ) ; return message ; } runMessageEnd ( message , numFiles ) { const numFilesString = ` (${ numFiles } files)` ; const suffix = numFiles <= 0 ? chalk . red ( numFilesString ) : numFiles > 1 ? chalk . green ( numFilesString ) : "" ; timeEnd ( message , suffix ) ; } } abstract class LanguageFixture extends Fixture { constructor ( language ) { super ( language ) ; } async setup ( ) { const setupCommand = this . language . setupCommand ; if ( ! setupCommand || ONLY_OUTPUT ) { return ; } console . error ( `* Setting up` , chalk . magenta ( this . name ) , `fixture` ) ; await inDir ( this . language . base , async ( ) => { await execAsync ( setupCommand ) ; } ) ; } abstract shouldSkipTest ( sample ) ; abstract async runQuicktype ( filename , additionalRendererOptions ) ; abstract async test ( filename , additionalRendererOptions , additionalFiles ) ; additionalFiles ( _sample ) { return [ ] ; } async runWithSample ( sample , index , total ) { const cwd = this . getRunDirectory ( ) ; const sampleFile = path . resolve ( sample . path ) ; const shouldSkip = this . shouldSkipTest ( sample ) ; const additionalFiles = this . additionalFiles ( sample ) . map ( ( p ) => path . resolve ( p ) ) ; const message = this . runMessageStart ( sample , index , total , cwd , shouldSkip ) ; if ( shouldSkip ) { return ; } shell . cp ( "-R" , this . language . base , cwd ) ; if ( this . language . copyInput ) { shell . cp ( sampleFile , cwd ) ; } let numFiles = - 1 ; await inDir ( cwd , async ( ) => { await this . runQuicktype ( sampleFile , sample . additionalRendererOptions ) ; if ( ONLY_OUTPUT ) { return ; } try { numFiles = await timeout ( this . test ( sampleFile , sample . additionalRendererOptions , additionalFiles ) , MAX_TEST_RUNTIME_MS ) ; } catch ( e ) { failWith ( "Fixture threw an exception" , { error : e , sample } ) ; } } ) ; if ( sample . saveOutput && OUTPUT_DIR !== undefined && this . language . output . indexOf ( "/" ) < 0 ) { const outputDir = path . join ( OUTPUT_DIR , this . language . name , path . dirname ( sample . path ) , path . basename ( sample . path , path . extname ( sample . path ) ) ) ; mkdirs ( outputDir ) ; shell . cp ( path . join ( cwd , this . language . output ) , outputDir ) ; } shell . rm ( "-rf" , cwd ) ; this . runMessageEnd ( message , numFiles ) ; } } class JSONFixture extends LanguageFixture { constructor ( language , public name = language . name ) { super ( language ) ; } runForName ( name ) { return this . name === name || name === "json" ; } async runQuicktype ( sample , additionalRendererOptions ) { await quicktypeForLanguage ( this . language , sample , "json" , true , additionalRendererOptions ) ; } async test ( filename , additionalRendererOptions , _additionalFiles ) { if ( this . language . compileCommand ) { await execAsync ( this . language . compileCommand ) ; } if ( this . language . runCommand === undefined ) return 0 ; compareJsonFileToJson ( comparisonArgs ( this . language , filename , filename , additionalRendererOptions ) ) ; if ( this . language . diffViaSchema && ! _ . includes ( this . language . skipDiffViaSchema , path . basename ( filename ) ) ) { debug ( "* Diffing with code generated via JSON Schema" ) ; await quicktype ( { src : [ filename ] , lang : "schema" , out : "schema.json" , topLevel : this . language . topLevel , rendererOptions : { } , } ) ; shell . mv ( this . language . output , `${ this . language . output }.expected` ) ; await quicktypeForLanguage ( this . language , "schema.json" , "schema" , true , additionalRendererOptions ) ; exec ( `diff -Naur ${ this . language . output }.expected ${ this . language . output } > /dev/null 2>&1` , undefined ) ; } return 1 ; } shouldSkipTest ( sample ) { if ( fs . statSync ( sample . path ) . size > 32 * 1024 * 1024 ) { return true ; } return _ . includes ( this . language . skipJSON , path . basename ( sample . path ) ) ; } getSamples ( sources ) { const prioritySamples = _ . concat ( testsInDir ( "test/inputs/json/priority" , "json" ) , testsInDir ( "test/inputs/json/samples" , "json" ) ) ; const miscSamples = this . language . skipMiscJSON ? [ ] : testsInDir ( "test/inputs/json/misc" , "json" ) ; let { priority , others } = samplesFromSources ( sources , prioritySamples , miscSamples , "json" ) ; const combinationInputs = _ . map ( [ 1 , 2 , 3 , 4 ] , ( n ) => _ . find ( prioritySamples , ( p ) => p . endsWith ( `/priority/combinations${ n }.json` ) ) ) ; if ( combinationInputs . some ( ( p ) => p === undefined ) ) { return failWith ( "priority/combinations[1234].json samples not found" , prioritySamples ) ; } if ( sources . length === 0 && ! ONLY_OUTPUT ) { const quickTestSamples = _ . chain ( this . language . quickTestRendererOptions ) . flatMap ( ( qt ) => { if ( Array . isArray ( qt ) ) { const [ filename , ro ] = qt ; const input = _ . find ( ( [ ] as string [ ] ) . concat ( prioritySamples , miscSamples ) , ( p ) => p . endsWith ( `/${ filename }` ) ) ; if ( input === undefined ) { return failWith ( `quick-test sample ${ filename } not found` , qt ) ; } return [ { path : input , additionalRendererOptions : ro , saveOutput : false , } , ] ; } else { return _ . map ( combinationInputs , ( p ) => ( { path : defined ( p ) , additionalRendererOptions : qt , saveOutput : false , } ) ) ; } } ) . value ( ) ; priority = quickTestSamples . concat ( priority ) ; } return { priority , others } ; } } class JSONToXToYFixture extends JSONFixture { private readonly runLanguage : languages . Language ; constructor ( private readonly _fixturePrefix , languageXName , languageXOutputFilename , rendererOptions , skipJSON , language ) { super ( { name : languageXName , base : language . base , setupCommand : language . setupCommand , runCommand : mustNotHappen , diffViaSchema : false , skipDiffViaSchema : [ ] , allowMissingNull : language . allowMissingNull , features : language . features , output : languageXOutputFilename , topLevel : "TopLevel" , skipJSON , skipMiscJSON : false , skipSchema : [ ] , rendererOptions , quickTestRendererOptions : [ ] , sourceFiles : language . sourceFiles , } ) ; this . runLanguage = language ; this . name = `${ this . _fixturePrefix }-${ language . name }` ; } runForName ( name ) { return this . name === name || name === this . _fixturePrefix ; } async test ( filename , additionalRendererOptions , _additionalFiles ) { await quicktypeForLanguage ( this . runLanguage , this . language . output , this . language . name , false , additionalRendererOptions ) ; compareJsonFileToJson ( comparisonArgs ( this . runLanguage , filename , filename , additionalRendererOptions ) ) ; return 1 ; } shouldSkipTest ( sample ) { if ( super . shouldSkipTest ( sample ) ) return true ; return _ . includes ( this . runLanguage . skipJSON , path . basename ( sample . path ) ) ; } } const dateTimeRecognizer = new DefaultDateTimeRecognizer ( ) ; class JSONSchemaJSONFixture extends JSONToXToYFixture { constructor ( language ) { const skipJSON = [ "blns-object.json" , "31189.json" , "437e7.json" , "ed095.json" , ] ; super ( "schema-json" , "schema" , "schema.json" , { } , skipJSON , language ) ; } async test ( filename , additionalRendererOptions , additionalFiles ) { let input = JSON . parse ( fs . readFileSync ( filename , "utf8" ) ) ; let schema = JSON . parse ( fs . readFileSync ( this . language . output , "utf8" ) ) ; let ajv = new Ajv ( { format : "full" , unknownFormats : [ "integer" , "boolean" ] } ) ; ajv . addFormat ( "date-time" , ( s ) => dateTimeRecognizer . isDateTime ( s ) ) ; let valid = ajv . validate ( schema , input ) ; if ( ! valid ) { failWith ( "Generated schema does not validate input JSON." , { filename , } ) ; } await super . test ( filename , additionalRendererOptions , additionalFiles ) ; let schemaSchema = "schema-from-schema.json" ; await quicktype ( { src : [ this . language . output ] , srcLang : this . language . name , lang : this . language . name , topLevel : this . language . topLevel , out : schemaSchema , rendererOptions : { } , } ) ; compareJsonFileToJson ( { expectedFile : this . language . output , given : { file : schemaSchema } , strict : true , } ) ; return 1 ; } } const skipTypeScriptTests = [ "no-classes.json" , "optional-union.json" , "pokedex.json" , "github-events.json" , "bug855-short.json" , "bug863.json" , "00c36.json" , "010b1.json" , "050b0.json" , "06bee.json" , "07c75.json" , "0a91a.json" , "10be4.json" , "13d8d.json" , "176f1.json" , "1a7f5.json" , "262f0.json" , "2df80.json" , "32d5c.json" , "33d2e.json" , "34702.json" , "3536b.json" , "3e9a3.json" , "3f1ce.json" , "43970.json" , "570ec.json" , "5eae5.json" , "65dec.json" , "66121.json" , "6dec6.json" , "6eb00.json" , "77392.json" , "7f568.json" , "7eb30.json" , "7fbfb.json" , "9847b.json" , "996bd.json" , "9a503.json" , "9eed5.json" , "a45b0.json" , "ab0d1.json" , "ad8be.json" , "ae9ca.json" , "af2d1.json" , "b4865.json" , "c8c7e.json" , "cb0cc.json" , "cda6c.json" , "dbfb3.json" , "e2a58.json" , "e53b5.json" , "e8a0b.json" , "e8b04.json" , "ed095.json" , "f3139.json" , "f3edf.json" , "f466a.json" , ] ; class JSONTypeScriptFixture extends JSONToXToYFixture { constructor ( language ) { super ( "json-ts" , "ts" , "typescript.ts" , { "just-types" : "true" } , [ ] , language ) ; } shouldSkipTest ( sample ) { if ( super . shouldSkipTest ( sample ) ) return true ; return skipTypeScriptTests . indexOf ( path . basename ( sample . path ) ) >= 0 ; } } class JSONSchemaFixture extends LanguageFixture { constructor ( language , readonly name = `schema-${ language . name }` ) { super ( language ) ; } runForName ( name ) { return this . name === name || name === "schema" ; } getSamples ( sources ) { const prioritySamples = testsInDir ( "test/inputs/schema/" , "schema" ) ; return samplesFromSources ( sources , prioritySamples , [ ] , "schema" ) ; } shouldSkipTest ( sample ) { return _ . includes ( this . language . skipSchema , path . basename ( sample . path ) ) ; } async runQuicktype ( filename , additionalRendererOptions ) { await quicktypeForLanguage ( this . language , filename , "schema" , false , additionalRendererOptions ) ; } additionalFiles ( sample ) { const baseName = pathWithoutExtension ( sample . path , ".schema" ) ; return additionalTestFiles ( baseName , "json" , this . language . features ) ; } async test ( _sample , additionalRendererOptions , additionalFiles ) { if ( this . language . compileCommand ) { await execAsync ( this . language . compileCommand ) ; } if ( this . language . runCommand === undefined ) return 0 ; const failExtensions = this . language . features . map ( ( f ) => `.fail.${ f }.json` ) . concat ( [ ".fail.json" ] ) ; for ( const filename of additionalFiles ) { if ( failExtensions . some ( ( ext ) => filename . endsWith ( ext ) ) ) { callAndExpectFailure ( `Expected failure on input ${ filename }` , ( ) => exec ( defined ( this . language . runCommand ) ( filename ) , runEnvForLanguage ( additionalRendererOptions ) , false ) . stdout ) ; } else { let expected = filename ; for ( const feature of this . language . features ) { const featureFilename = filename . replace ( ".json" , `.out.${ feature }.json` ) ; if ( fs . existsSync ( featureFilename ) ) { expected = featureFilename ; break ; } } compareJsonFileToJson ( comparisonArgs ( this . language , filename , expected , additionalRendererOptions ) ) ; } } return additionalFiles . length ; } } function graphQLSchemaFilename ( baseName ) { const baseMatch = baseName . match ( /(.*\D)\d+$/ ) ; if ( baseMatch === null ) { return failWith ( "GraphQL test filename does not correspond to naming schema" , { baseName } ) ; } return baseMatch [ 1 ] + ".gqlschema" ; } class GraphQLFixture extends LanguageFixture { constructor ( language , private readonly _onlyExactName = false , readonly name = `graphql-${ language . name }` ) { super ( language ) ; } runForName ( name ) { return this . name === name || ( ! this . _onlyExactName && name === "graphql" ) ; } getSamples ( sources ) { const prioritySamples = testsInDir ( "test/inputs/graphql/" , "graphql" ) ; return samplesFromSources ( sources , prioritySamples , [ ] , "graphql" ) ; } shouldSkipTest ( _sample ) { return false ; } async runQuicktype ( filename , additionalRendererOptions ) { const baseName = pathWithoutExtension ( filename , ".graphql" ) ; const schemaFilename = graphQLSchemaFilename ( baseName ) ; await quicktypeForLanguage ( this . language , filename , "graphql" , false , additionalRendererOptions , schemaFilename ) ; } additionalFiles ( sample ) { const baseName = pathWithoutExtension ( sample . path , ".graphql" ) ; return additionalTestFiles ( baseName , "json" ) ; } async test ( _filename , additionalRendererOptions , additionalFiles ) { if ( this . language . compileCommand ) { await execAsync ( this . language . compileCommand ) ; } if ( this . language . runCommand === undefined ) return 0 ; for ( const fn of additionalFiles ) { compareJsonFileToJson ( comparisonArgs ( this . language , fn , fn , additionalRendererOptions ) ) ; } return additionalFiles . length ; } } class CommandSuccessfulLanguageFixture extends LanguageFixture { constructor ( language , public name = language . name ) { super ( language ) ; } runForName ( name ) { return this . name === name || name === "json" ; } async runQuicktype ( sample , additionalRendererOptions ) { await quicktypeForLanguage ( this . language , sample , "json" , true , additionalRendererOptions ) ; } async test ( filename , _additionalRendererOptions , _additionalFiles ) { if ( this . language . compileCommand ) { await execAsync ( this . language . compileCommand ) ; } if ( this . language . runCommand === undefined ) { throw new Error ( "Invalid run command." ) ; } const command = this . language . runCommand ( filename ) ; const results = await execAsync ( command ) ; if ( results . stdout . indexOf ( "Success" ) === - 1 ) { throw new Error ( `Test failed:\n${ results . stdout }` ) ; } return 0 ; } shouldSkipTest ( sample ) { if ( fs . statSync ( sample . path ) . size > 32 * 1024 * 1024 ) { return true ; } return _ . includes ( this . language . skipJSON , path . basename ( sample . path ) ) ; } getSamples ( sources ) { const prioritySamples = _ . concat ( testsInDir ( "test/inputs/json/priority" , "json" ) , testsInDir ( "test/inputs/json/samples" , "json" ) ) ; const miscSamples = this . language . skipMiscJSON ? [ ] : testsInDir ( "test/inputs/json/misc" , "json" ) ; let { priority , others } = samplesFromSources ( sources , prioritySamples , miscSamples , "json" ) ; const combinationInputs = _ . map ( [ 1 , 2 , 3 , 4 ] , ( n ) => _ . find ( prioritySamples , ( p ) => p . endsWith ( `/priority/combinations${ n }.json` ) ) ) ; if ( combinationInputs . some ( ( p ) => p === undefined ) ) { return failWith ( "priority/combinations[1234].json samples not found" , prioritySamples ) ; } if ( sources . length === 0 && ! ONLY_OUTPUT ) { const quickTestSamples = _ . chain ( this . language . quickTestRendererOptions ) . flatMap ( ( qt ) => { if ( Array . isArray ( qt ) ) { const [ filename , ro ] = qt ; const input = _ . find ( ( [ ] as string [ ] ) . concat ( prioritySamples , miscSamples ) , ( p ) => p . endsWith ( `/${ filename }` ) ) ; if ( input === undefined ) { return failWith ( `quick-test sample ${ filename } not found` , qt ) ; } return [ { path : input , additionalRendererOptions : ro , saveOutput : false , } , ] ; } else { return _ . map ( combinationInputs , ( p ) => ( { path : defined ( p ) , additionalRendererOptions : qt , saveOutput : false , } ) ) ; } } ) . value ( ) ; priority = quickTestSamples . concat ( priority ) ; } return { priority , others } ; } } export const allFixtures = [ new JSONFixture ( languages . CSharpLanguage ) , new JSONFixture ( languages . JavaLanguage ) , new JSONFixture ( languages . JavaLanguageWithLegacyDateTime , "java-datetime-legacy" ) , new JSONFixture ( languages . JavaLanguageWithLombok , "java-lombok" ) , new JSONFixture ( languages . GoLanguage ) , new JSONFixture ( languages . CPlusPlusLanguage ) , new JSONFixture ( languages . RustLanguage ) , new JSONFixture ( languages . RubyLanguage ) , new JSONFixture ( languages . PythonLanguage ) , new JSONFixture ( languages . ElmLanguage ) , new JSONFixture ( languages . SwiftLanguage ) , new JSONFixture ( languages . ObjectiveCLanguage ) , new JSONFixture ( languages . TypeScriptLanguage ) , new JSONFixture ( languages . FlowLanguage ) , new JSONFixture ( languages . JavaScriptLanguage ) , new JSONFixture ( languages . KotlinLanguage ) , new JSONFixture ( languages . KotlinJacksonLanguage , "kotlin-jackson" ) , new JSONFixture ( languages . DartLanguage ) , new JSONFixture ( languages . PikeLanguage ) , new JSONFixture ( languages . HaskellLanguage ) , new JSONSchemaJSONFixture ( languages . CSharpLanguage ) , new JSONTypeScriptFixture ( languages . CSharpLanguage ) , new JSONSchemaFixture ( languages . CSharpLanguage ) , new JSONSchemaFixture ( languages . JavaLanguage ) , new JSONSchemaFixture ( languages . JavaLanguageWithLegacyDateTime , "schema-java-datetime-legacy" ) , new JSONSchemaFixture ( languages . JavaLanguageWithLombok , "schema-java-lombok" ) , new JSONSchemaFixture ( languages . GoLanguage ) , new JSONSchemaFixture ( languages . CPlusPlusLanguage ) , new JSONSchemaFixture ( languages . RustLanguage ) , new JSONSchemaFixture ( languages . RubyLanguage ) , new JSONSchemaFixture ( languages . PythonLanguage ) , new JSONSchemaFixture ( languages . ElmLanguage ) , new JSONSchemaFixture ( languages . SwiftLanguage ) , new JSONSchemaFixture ( languages . TypeScriptLanguage ) , new JSONSchemaFixture ( languages . FlowLanguage ) , new JSONSchemaFixture ( languages . JavaScriptLanguage ) , new JSONSchemaFixture ( languages . KotlinLanguage ) , new JSONSchemaFixture ( languages . KotlinJacksonLanguage , "schema-kotlin-jackson" ) , new JSONSchemaFixture ( languages . DartLanguage ) , new JSONSchemaFixture ( languages . PikeLanguage ) , new JSONSchemaFixture ( languages . HaskellLanguage ) , new GraphQLFixture ( languages . CSharpLanguage ) , new GraphQLFixture ( languages . JavaLanguage ) , new GraphQLFixture ( languages . JavaLanguageWithLegacyDateTime , false , "graphql-java-datetime-legacy" ) , new GraphQLFixture ( languages . JavaLanguageWithLombok , false , "graphql-java-lombok" ) , new GraphQLFixture ( languages . GoLanguage ) , new GraphQLFixture ( languages . CPlusPlusLanguage ) , new GraphQLFixture ( languages . PythonLanguage ) , new GraphQLFixture ( languages . SwiftLanguage ) , new GraphQLFixture ( languages . ObjectiveCLanguage , true ) , new GraphQLFixture ( languages . TypeScriptLanguage ) , new GraphQLFixture ( languages . FlowLanguage ) , new GraphQLFixture ( languages . JavaScriptLanguage ) , new GraphQLFixture ( languages . DartLanguage ) , new GraphQLFixture ( languages . PikeLanguage ) , new GraphQLFixture ( languages . HaskellLanguage ) , new CommandSuccessfulLanguageFixture ( languages . JavaScriptPropTypesLanguage ) , ] ;