/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import path = require('path'); import assert = require('assert'); import { join, normalize } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { IRawFileMatch, IFolderSearch } from 'vs/workbench/services/search/node/search'; const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; const ROOT_FOLDER_QUERY: IFolderSearch[] = [ TEST_ROOT_FOLDER ]; const ROOT_FOLDER_QUERY_36438: IFolderSearch[] = [ { folder: path.normalize(require.toUrl('./fixtures2/36438')) } ]; const MULTIROOT_QUERIES: IFolderSearch[] = [ { folder: EXAMPLES_FIXTURES }, { folder: MORE_FIXTURES } ]; const testTimeout = 5000; suite('FileSearchEngine', () => { test('Files: *.js', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.js' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 4); done(); }); }); test('Files: maxResults', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, maxResults: 1 }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: maxResults without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, maxResults: 1, useRipgrep: false }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/file.txt': true }, exists: true }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 0); assert.ok(complete.limitHit); done(); }); }); test('Files: not exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/nofile.txt': true }, exists: true }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 0); assert.ok(!complete.limitHit); done(); }); }); test('Files: exists without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/file.txt': true }, exists: true, useRipgrep: false }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 0); assert.ok(complete.limitHit); done(); }); }); test('Files: not exists without Ripgrep', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/nofile.txt': true }, exists: true, useRipgrep: false }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 0); assert.ok(!complete.limitHit); done(); }); }); test('Files: examples/com*', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: normalize(join('examples', 'com*'), true) }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: examples (fuzzy)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'xl' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 7); done(); }); }); test('Files: multiroot', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: MULTIROOT_QUERIES, filePattern: 'file' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 3); done(); }); }); test('Files: multiroot with includePattern and maxResults', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: MULTIROOT_QUERIES, maxResults: 1, includePattern: { '*.txt': true, '*.js': true }, useRipgrep: true }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: multiroot with includePattern and exists', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: MULTIROOT_QUERIES, exists: true, includePattern: { '*.txt': true, '*.js': true }, useRipgrep: true }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error, complete) => { assert.ok(!error); assert.equal(count, 0); assert.ok(complete.limitHit); done(); }); }); test('Files: NPE (CamelCase)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'NullPE' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: *.*', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 14); done(); }); }); test('Files: *.as', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.as' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 0); done(); }); }); test('Files: *.* without derived', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'site.*', excludePattern: { '**/*.css': { 'when': '$(basename).less' } } }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.strictEqual(path.basename(res.relativePath), 'site.less'); done(); }); }); test('Files: *.* exclude folder without wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { 'examples': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 8); done(); }); }); test('Files: exclude folder without wildcard #36438', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY_36438, excludePattern: { 'modules': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: include folder without wildcard #36438', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY_36438, includePattern: { 'modules/**': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); test('Files: *.* exclude folder with leading wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { '**/examples': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 8); done(); }); }); test('Files: *.* exclude folder with trailing wildcard', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { 'examples/**': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 8); done(); }); }); test('Files: *.* exclude with unicode', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', excludePattern: { '**/üm laut汉语': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 13); done(); }); }); test('Files: multiroot with exclude', function (done: () => void) { this.timeout(testTimeout); const folderQueries: IFolderSearch[] = [ { folder: EXAMPLES_FIXTURES, excludePattern: { '**/anotherfile.txt': true } }, { folder: MORE_FIXTURES, excludePattern: { '**/file.txt': true } } ]; const engine = new FileSearchEngine({ folderQueries, filePattern: '*' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 5); done(); }); }); test('Files: Unicode and Spaces', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '汉语' }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), '汉语.txt'); done(); }); }); test('Files: no results', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'nofilematch' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 0); done(); }); }); test('Files: absolute path to file ignores excludes', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), excludePattern: { '**/*.css': true } }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), 'site.css'); done(); }); }); test('Files: relative path matched once', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join('examples', 'company.js')) }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), 'company.js'); done(); }); }); test('Files: relative path to file ignores excludes', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join('examples', 'company.js')), excludePattern: { '**/*.js': true } }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), 'company.js'); done(); }); }); test('Files: Include pattern, single files', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { 'site.css': true, 'examples/company.js': true, 'examples/subfolder/subfile.txt': true } }); let res: IRawFileMatch[] = []; engine.search((result) => { res.push(result); }, () => { }, (error) => { assert.ok(!error); const basenames = res.map(r => path.basename(r.relativePath)); assert.ok(basenames.indexOf('site.css') !== -1, `site.css missing in ${JSON.stringify(basenames)}`); assert.ok(basenames.indexOf('company.js') !== -1, `company.js missing in ${JSON.stringify(basenames)}`); assert.ok(basenames.indexOf('subfile.txt') !== -1, `subfile.txt missing in ${JSON.stringify(basenames)}`); done(); }); }); test('Files: extraFiles only', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) ], filePattern: '*.js' }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), 'company.js'); done(); }); }); test('Files: extraFiles only (with include)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) ], filePattern: '*.*', includePattern: { '**/*.css': true } }); let count = 0; let res: IRawFileMatch; engine.search((result) => { if (result) { count++; } res = result; }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); assert.equal(path.basename(res.relativePath), 'site.css'); done(); }); }); test('Files: extraFiles only (with exclude)', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) ], filePattern: '*.*', excludePattern: { '**/*.css': true } }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 2); done(); }); }); test('Files: no dupes in nested folders', function (done: () => void) { this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [ { folder: EXAMPLES_FIXTURES }, { folder: path.join(EXAMPLES_FIXTURES, 'subfolder') } ], filePattern: 'subfile.txt' }); let count = 0; engine.search((result) => { if (result) { count++; } }, () => { }, (error) => { assert.ok(!error); assert.equal(count, 1); done(); }); }); }); suite('FileWalker', () => { test('Find: exclude subfolder', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const file0 = './more/file.txt'; const file1 = './examples/subfolder/subfile.txt'; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); done(); }); }); }); test('Find: folder excludes', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const folderQueries: IFolderSearch[] = [ { folder: TEST_FIXTURES, excludePattern: { '**/subfolder': true } } ]; const file0 = './more/file.txt'; const file1 = './examples/subfolder/subfile.txt'; const walker = new FileWalker({ folderQueries }); const cmd1 = walker.spawnFindCmd(folderQueries[0]); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert(outputContains(stdout1, file0), stdout1); assert(!outputContains(stdout1, file1), stdout1); done(); }); }); test('Find: exclude multiple folders', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const file0 = './index.html'; const file1 = './examples/small.js'; const file2 = './more/file.txt'; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file2), -1, stdout1); const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '{**/examples,**/more}': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); assert.strictEqual(stdout2.split('\n').indexOf(file2), -1, stdout2); done(); }); }); }); test('Find: exclude folder path suffix', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const file0 = './examples/company.js'; const file1 = './examples/subfolder/subfile.txt'; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); done(); }); }); }); test('Find: exclude subfolder path suffix', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const file0 = './examples/subfolder/subfile.txt'; const file1 = './examples/subfolder/anotherfolder/anotherfile.txt'; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); done(); }); }); }); test('Find: exclude folder path', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const file0 = './examples/company.js'; const file1 = './examples/subfolder/subfile.txt'; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2.split('\n').indexOf(file1), -1, stdout2); done(); }); }); }); test('Find: exclude combination of paths', function (done: () => void) { this.timeout(testTimeout); if (platform.isWindows) { done(); return; } const filesIn = [ './examples/subfolder/subfile.txt', './examples/company.js', './index.html' ]; const filesOut = [ './examples/subfolder/anotherfolder/anotherfile.txt', './more/file.txt' ]; const walker = new FileWalker({ folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true, '**/something/else': true, '**/more': true, '**/andmore': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { assert.equal(err1, null); for (const fileIn of filesIn) { assert.notStrictEqual(stdout1.split('\n').indexOf(fileIn), -1, stdout1); } for (const fileOut of filesOut) { assert.strictEqual(stdout1.split('\n').indexOf(fileOut), -1, stdout1); } done(); }); }); function outputContains(stdout: string, ...files: string[]): boolean { const lines = stdout.split('\n'); return files.every(file => lines.indexOf(file) >= 0); } });