'use strict' ; import * as assert from 'power-assert' ; import * as _ from 'lodash' ; import * as mock from 'mock-fs' ; import * as path from 'path' ; import * as ts from 'typescript' ; import { ILinterOptions , IRuleMetadata , Linter , LintResult , RuleFailure } from 'tslint' ; import { ITsLinterOption , TsLinter } from '../tsLinter' ; import { IConfig , IIssue } from '../codeclimate' ; import { ContentRenderer } from '../contentRenderer' ; import { IConfigurationFile } from 'tslint/lib/configuration' ; import Utils , { temporaryDir } from '../utils' ; import { Observable } from 'rxjs' ; import { catchError , map , toArray } from 'rxjs/operators' ; describe ( 'TsLinter' , ( ) => { const linterPath = './' ; const targetPath = '/base/path/' ; const option = { targetPath , linterPath , codeClimateConfig : { include_paths : [ ] } , rules : [ ] } ; const templateFile = path . join ( linterPath , ContentRenderer . templateFileName ) ; const defaultTsLintFile = path . join ( linterPath , TsLinter . defaultTsLintFileName ) ; function mockFiles ( ... files ) { mock ( _ . reduce < string , any > ( files . concat ( defaultTsLintFile , templateFile ) , ( acc , file ) => { acc [ file ] = '{}' ; return acc ; } , { [ temporaryDir ] : { } } ) ) ; } afterEach ( ( ) => mock . restore ( ) ) ; describe ( '.getTsLintFile()' , ( ) => { it ( 'returns a specified file on the target' , ( ) => { const config = 'specified.json' ; const codeClimateConfig = { config , include_paths : [ ] } ; const expected = path . join ( targetPath , config ) ; mockFiles ( expected ) ; const tsLinter = new TsLinter ( { ... option , codeClimateConfig } ) ; assert . equal ( tsLinter . getTsLintFile ( ) , expected ) ; } ) ; it ( 'returns a default file on the linter when a specified file on the target doe not exist' , ( ) => { const config = 'non-existent.json' ; const codeClimateConfig = { config , include_paths : [ ] } ; const targetDefaultTsLintFile = path . join ( targetPath , TsLinter . defaultTsLintFileName ) ; mockFiles ( targetDefaultTsLintFile ) ; const tsLinter = new TsLinter ( { ... option , codeClimateConfig } ) ; assert . equal ( tsLinter . getTsLintFile ( ) , defaultTsLintFile ) ; } ) ; it ( 'returns a default file on the linter when no file on the target exists' , ( ) => { const config = 'non-existent.json' ; const codeClimateConfig = { config , include_paths : [ ] } ; mockFiles ( ) ; const tsLinter = new TsLinter ( { ... option , codeClimateConfig } ) ; assert . equal ( tsLinter . getTsLintFile ( ) , defaultTsLintFile ) ; } ) ; it ( 'returns a default file on the target' , ( ) => { const targetDefaultTsLintFile = path . join ( targetPath , TsLinter . defaultTsLintFileName ) ; mockFiles ( targetDefaultTsLintFile ) ; const tsLinter = new TsLinter ( option ) ; assert . equal ( tsLinter . getTsLintFile ( ) , targetDefaultTsLintFile ) ; } ) ; it ( 'returns a default file on the linter' , ( ) => { mockFiles ( ) ; const tsLinter = new TsLinter ( option ) ; assert . equal ( tsLinter . getTsLintFile ( ) , defaultTsLintFile ) ; } ) ; } ) ; describe ( '.listFiles()' , ( ) => { it ( 'returns file list' , ( ) => { mockFiles ( ) ; const include_paths = [ 'one.ts' , 'two.ts' ] ; const codeClimateConfig = { ... option . codeClimateConfig , include_paths } ; const tsLinter = new TsLinter ( { ... option , codeClimateConfig } ) ; tsLinter . fileMatcher . matchFiles = includePaths => includePaths . concat ( 'three.ts' ) ; const actual = tsLinter . listFiles ( ) ; assert . deepStrictEqual ( actual , include_paths . concat ( 'three.ts' ) ) ; } ) ; } ) ; describe ( '.lint()' , ( ) => { class MockedLinter extends Linter { constructor ( options , private dummyFailures ) { super ( options ) ; } lint ( fileName , source , configuration ? ) { } getResult ( ) { return { errorCount : 2 , warningCount : 0 , failures : this . dummyFailures , format : 'json' , output : 'output' } ; } } class MockedTsLinter extends TsLinter { constructor ( codeClimateConfig , private dummyFailures ) { super ( { targetPath , linterPath , codeClimateConfig , rules } ) ; } listFiles ( ) { return [ 'file.ts' ] ; } protected createLinter ( ) { return new MockedLinter ( this . linterOption , this . dummyFailures ) ; } } function createSourceFile ( fileName ) { const source = `'use strict';
      var unused = 32;
      let object = {
        c: 42,
        a: false
      };
      ` ; return ts . createSourceFile ( `${ targetPath }${ fileName }` , source , ts . ScriptTarget . ES2016 ) ; } function createIssue ( ) { return { type : 'issue' , check_name : ruleName , content : { body : '' } , description : 'some failure' , categories : [ 'Style' ] , remediation_points : 50000 , location : { path : sourceFile , positions : { begin : { line : 1 , column : 1 } , end : { line : 1 , column : 1 } } } } ; } function assertLintResult ( actual$ , expected , done ) { actual$ . pipe ( map ( ( result ) => { result . content = { body : '' } ; return result ; } ) , toArray ( ) , catchError ( assert . fail . bind ( assert ) ) ) . subscribe ( ( actual ) => { assert . deepStrictEqual ( actual , expected ) ; done ( ) ; } ) ; } const ruleName = 'foo-rule' ; const rules = [ { description : 'foo' , ruleName , type : 'style' , typescriptOnly : true , options : null , optionsDescription : '' , optionExamples : [ ] } ] ; const sourceFile = 'failed.ts' ; const file = createSourceFile ( sourceFile ) ; it ( 'passes' , done => { const tslintPath = path . join ( linterPath , TsLinter . defaultTsLintFileName ) ; mockFiles ( tslintPath , 'file.ts' ) ; const location = { path : sourceFile , positions : { begin : { line : 1 , column : 2 } , end : { line : 2 , column : 7 } } } ; const issue = { ... createIssue ( ) , location } ; const failure = new RuleFailure ( file , 1 , 20 , issue . description , ruleName ) ; const tsLinter = new MockedTsLinter ( { include_paths : [ ] } , [ failure ] ) ; const expected = [ issue ] ; const actual = tsLinter . lint ( ) ; assertLintResult ( actual , expected , done ) ; } ) ; it ( 'doesn\'t report warnings if ignore_warnings option is given' , done => { const tslintPath = path . join ( linterPath , TsLinter . defaultTsLintFileName ) ; mockFiles ( tslintPath , 'file.ts' ) ; const warningIssue = { ... createIssue ( ) , description : 'some warning' } ; const errorIssue = { ... createIssue ( ) , description : 'some error' } ; const warning = new RuleFailure ( file , 0 , 0 , warningIssue . description , ruleName ) ; warning . setRuleSeverity ( 'warning' ) ; const error = new RuleFailure ( file , 0 , 0 , errorIssue . description , ruleName ) ; error . setRuleSeverity ( 'error' ) ; const tsLinter = new MockedTsLinter ( { include_paths : [ ] , ignore_warnings : true } , [ warning , error ] ) ; const expected = [ errorIssue ] ; const actual = tsLinter . lint ( ) ; assertLintResult ( actual , expected , done ) ; } ) ; it ( 'returns runtime-error issue when failing to convert a rule failure to an issue' , done => { const tslintPath = path . join ( linterPath , TsLinter . defaultTsLintFileName ) ; mockFiles ( tslintPath , 'file.ts' ) ; const failure = new RuleFailure ( file , 0 , 0 , 'whatever' , 'non-existent' ) ; const tsLinter = new MockedTsLinter ( { include_paths : [ ] } , [ failure ] ) ; const error = new Error ( ) ; tsLinter . issueConverter . convert = ( ) => { throw error ; } ; const issue = { ... Utils . createIssueFromError ( error , tsLinter . getRelativeFilePath ( 'file.ts' ) ) , content : { body : '' } } ; const expected = [ issue ] ; const actual = tsLinter . lint ( ) ; assertLintResult ( actual , expected , done ) ; } ) ; } ) ; describe ( '.getRelativeFilePath' , ( ) => { it ( 'returns correct path' , ( ) => { const tsLinter = new TsLinter ( { ... option , targetPath : '/code/src' } ) ; assert ( tsLinter . getRelativeFilePath ( '/code/src/foo.ts' ) === 'foo.ts' ) ; assert ( tsLinter . getRelativeFilePath ( '/code/src/foo/bar.ts' ) === 'foo/bar.ts' ) ; assert ( tsLinter . getRelativeFilePath ( '/code/src/foo/bar/baz.ts' ) === 'foo/bar/baz.ts' ) ; assert ( tsLinter . getRelativeFilePath ( '/code/tmp/foo.ts' ) === '../tmp/foo.ts' ) ; assert ( tsLinter . getRelativeFilePath ( '/tmp/foo.ts' ) === '../../tmp/foo.ts' ) ; } ) ; } ) ; } ) ;