diff --git a/lib/Report.js b/lib/Report.js index 09582148..122c2aa5 100644 --- a/lib/Report.js +++ b/lib/Report.js @@ -4,8 +4,6 @@ // https://github.com/prettier/prettier/issues/2597 const Report /*: 'console' | 'json' | 'junit' */ = 'console'; -const all = ['json', 'junit', 'console']; - function isMachineReadable(report /*: typeof Report */) /*: boolean */ { switch (report) { case 'json': @@ -18,6 +16,5 @@ function isMachineReadable(report /*: typeof Report */) /*: boolean */ { module.exports = { Report, - all, isMachineReadable, }; diff --git a/lib/RunTests.js b/lib/RunTests.js index 42e34320..a5c5fca7 100644 --- a/lib/RunTests.js +++ b/lib/RunTests.js @@ -1,10 +1,7 @@ // @flow const chalk = require('chalk'); -const chokidar = require('chokidar'); const path = require('path'); -const readline = require('readline'); -const packageInfo = require('../package.json'); const Compile = require('./Compile'); const { DependencyProvider } = require('./DependencyProvider.js'); const ElmJson = require('./ElmJson'); @@ -43,40 +40,32 @@ function getPipeFilename(runsExecuted /*: number */) /*: string */ { // `elm.json changed > Compiling-- NAMING ERROR - File.elm` // Also note that using too much `clearLine` or `clearConsole` causes flickering // on Windows, so it's nicer to cleverly overwrite old output when possible. -function makeProgressLogger( - report /*: typeof Report.Report */, - clearConsole /*: boolean */ -) { +function makeHumanReadableProgressLogger(clearConsole /*: boolean */) { + let readline; const items = []; + return { log(message) { items.push(message); - if (!Report.isMachineReadable(report)) { - process.stdout.write(`${items.join(' > ')}\r`); - } + process.stdout.write(`${items.join(' > ')}\r`); }, newLine() { items.length = 0; - if (!Report.isMachineReadable(report)) { - process.stdout.write('\n'); - } + process.stdout.write('\n'); }, overwrite(message) { items.length = 0; items.push(message); - if (!Report.isMachineReadable(report)) { - process.stdout.write(`${message}\r`); - } + process.stdout.write(`${message}\r`); }, clearLine() { items.length = 0; - if (!Report.isMachineReadable(report)) { - readline.clearLine(process.stdout, 0); - } + readline = readline || require('readline'); + readline.clearLine(process.stdout, 0); }, clearConsole() { items.length = 0; - if (!Report.isMachineReadable(report) && clearConsole) { + if (clearConsole) { process.stdout.write( process.platform === 'win32' ? '\x1B[2J\x1B[0f' @@ -87,6 +76,16 @@ function makeProgressLogger( }; } +function makeDummyProgressLogger() { + return { + log(message) {}, // eslint-disable-line no-unused-vars + newLine() {}, + overwrite(message) {}, // eslint-disable-line no-unused-vars + clearLine() {}, + clearConsole() {}, + }; +} + function diffArrays/*:: */( from /*: Array */, to /*: Array */ @@ -109,7 +108,12 @@ const Queue /*: Array<{ }> */ = []; void Queue; -function watcherEventMessage(queue /*: typeof Queue */) /*: string */ { +function watcherEventMessage( + isHumanReadable /*: boolean */, + queue /*: typeof Queue */ +) /*: string */ { + if (!isHumanReadable) return ''; + const filePaths = Array.from(new Set(queue.map(({ filePath }) => filePath))); if (filePaths.length === 1) { const { event, filePath } = queue[0]; @@ -123,6 +127,7 @@ function watcherEventMessage(queue /*: typeof Queue */) /*: string */ { function runTests( dependencyProvider /*: DependencyProvider */, projectRootDir /*: string */, + packageVersion /*: string */, pathToElmBinary /*: string */, testFileGlobs /*: Array */, processes /*: number */, @@ -146,7 +151,10 @@ function runTests( let currentRun /*: Promise | void */ = undefined; let queue /*: typeof Queue */ = []; - const progressLogger = makeProgressLogger(report, clearConsole); + const isHumanReadable = !Report.isMachineReadable(report); + const progressLogger = isHumanReadable + ? makeHumanReadableProgressLogger(clearConsole) + : makeDummyProgressLogger(); async function run() /*: Promise */ { try { @@ -171,7 +179,7 @@ function runTests( // Re-print the message in case the queue has become longer while waiting. if (queue.length > queueLengthBeforeWaiting) { progressLogger.clearLine(); - progressLogger.log(watcherEventMessage(queue)); + progressLogger.log(watcherEventMessage(isHumanReadable, queue)); } } @@ -184,7 +192,7 @@ function runTests( // Files may be changed, added or removed so always re-create project info // from disk to stay fresh. - const project = Project.init(projectRootDir, packageInfo.version); + const project = Project.init(projectRootDir, packageVersion); ElmJson.requireElmTestPackage(projectRootDir, project.elmJson); Project.validateTestsSourceDirs(project); @@ -241,7 +249,7 @@ function runTests( progressLogger.newLine(); return await Supervisor.run( - packageInfo.version, + packageVersion, pipeFilename, report, processes, @@ -261,10 +269,10 @@ function runTests( const onRunFinish = () => { if (queue.length > 0) { progressLogger.clearConsole(); - progressLogger.log(watcherEventMessage(queue)); + progressLogger.log(watcherEventMessage(isHumanReadable, queue)); currentRun = run().then(onRunFinish); } else { - if (!Report.isMachineReadable(report)) { + if (isHumanReadable) { console.log(chalk.blue('Watching for changes...')); } currentRun = undefined; @@ -295,12 +303,13 @@ function runTests( const separator = '='.repeat(message.length); console.log(chalk.blue(`\n\n\n${message}\n${separator}\n\n\n`)); } - progressLogger.log(watcherEventMessage(queue)); + progressLogger.log(watcherEventMessage(isHumanReadable, queue)); currentRun = run().then(onRunFinish); } } }; + const chokidar = require('chokidar'); watcher = chokidar.watch(alwaysWatched, { ignoreInitial: true, ignored: FindTests.ignoredDirsGlobs, diff --git a/lib/Solve.js b/lib/Solve.js index 21a7e397..221dbf7b 100644 --- a/lib/Solve.js +++ b/lib/Solve.js @@ -1,6 +1,5 @@ // @flow -const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const ElmJson = require('./ElmJson'); @@ -13,6 +12,7 @@ void Project; void DependencyProvider; function sha256(string) { + const crypto = require('crypto'); return crypto.createHash('sha256').update(string).digest('hex'); } diff --git a/lib/elm-test.js b/lib/elm-test.js index 1127b77c..028a0424 100644 --- a/lib/elm-test.js +++ b/lib/elm-test.js @@ -1,22 +1,8 @@ // @flow const { InvalidOptionArgumentError, Option, program } = require('commander'); -const fs = require('fs'); -const os = require('os'); const path = require('path'); -const which = require('which'); const packageInfo = require('../package.json'); -const Compile = require('./Compile'); -const { DependencyProvider } = require('./DependencyProvider.js'); -const ElmJson = require('./ElmJson'); -const FindTests = require('./FindTests'); -const Generate = require('./Generate'); -const Install = require('./Install'); -const Project = require('./Project'); -const Report = require('./Report'); -const RunTests = require('./RunTests'); - -void Report; const parsePositiveInteger = (minimum /*: number */) => @@ -34,12 +20,14 @@ const parsePositiveInteger = }; function findClosestElmJson(dir /*: string */) /*: string | void */ { - const entry = ElmJson.getPath(dir); - return fs.existsSync(entry) - ? entry - : dir === path.parse(dir).root - ? undefined - : findClosestElmJson(path.dirname(dir)); + const fs = require('fs'); + const ElmJson = require('./ElmJson'); + do { + const entry = ElmJson.getPath(dir); + if (fs.existsSync(entry)) return entry; + dir = path.dirname(dir); + } while (dir !== path.parse(dir).root); + return undefined; } function getProjectRootDir(subcommand /*: string */) /*: string */ { @@ -55,7 +43,8 @@ function getProjectRootDir(subcommand /*: string */) /*: string */ { return path.dirname(elmJsonPath); } -function getProject(subcommand /*: string */) /*: typeof Project.Project */ { +function getProject(subcommand /*: string */) { + const Project = require('./Project'); try { return Project.init(getProjectRootDir(subcommand), packageInfo.version); } catch (error) { @@ -66,6 +55,7 @@ function getProject(subcommand /*: string */) /*: typeof Project.Project */ { function getPathToElmBinary(compiler /*: string | void */) /*: string */ { const name = compiler === undefined ? 'elm' : compiler; + const which = require('which'); try { return path.resolve(which.sync(name)); } catch (_error) { @@ -99,8 +89,6 @@ elm-test "src/**/*Tests.elm" `.trim(); function main() { - const dependencyProvider = new DependencyProvider(); - process.title = 'elm-test'; program @@ -133,7 +121,7 @@ function main() { 'Specify which format to use for reporting test results' ) .default('console') - .choices(Report.all) + .choices(['json', 'junit', 'console']) ) // `chalk.supportsColor` looks at `process.argv` for these flags. // We still need to define them so they appear in `--help` and aren’t @@ -168,15 +156,16 @@ function main() { program .command('init') - .description( - `Install ${ElmJson.ELM_TEST_PACKAGE} and create tests/Example.elm` - ) + .description('Install elm-explorations/test and create tests/Example.elm') .action(() => { + const fs = require('fs'); + const Install = require('./Install'); + const options = program.opts(); const pathToElmBinary = getPathToElmBinary(options.compiler); const project = getProject('init'); try { - Install.install(project, pathToElmBinary, ElmJson.ELM_TEST_PACKAGE); + Install.install(project, pathToElmBinary, 'elm-explorations/test'); fs.mkdirSync(project.testsDir, { recursive: true }); fs.copyFileSync( path.join(__dirname, '..', 'templates', 'tests', 'Example.elm'), @@ -187,7 +176,7 @@ function main() { throw process.exit(1); } console.log( - `\nCheck out the documentation for getting started at https://package.elm-lang.org/packages/${ElmJson.ELM_TEST_PACKAGE}/latest` + '\nCheck out the documentation for getting started at https://package.elm-lang.org/packages/elm-explorations/test/latest' ); process.exit(0); }); @@ -198,6 +187,8 @@ function main() { 'Like `elm install package`, except it installs to "test-dependencies" in your elm.json' ) .action((packageName) => { + const Install = require('./Install'); + const options = program.opts(); const pathToElmBinary = getPathToElmBinary(options.compiler); const project = getProject('install'); @@ -223,6 +214,12 @@ function main() { const pathToElmBinary = getPathToElmBinary(options.compiler); const project = getProject('make'); const make = async () => { + const Generate = require('./Generate'); + const FindTests = require('./FindTests'); + const Compile = require('./Compile'); + const { DependencyProvider } = require('./DependencyProvider.js'); + const dependencyProvider = new DependencyProvider(); + Generate.generateElmJson(dependencyProvider, project); await Compile.compileSources( FindTests.resolveGlobs( @@ -248,13 +245,19 @@ function main() { // command and only run tests in `src/`, ignoring all files in `tests/`. .command('__elmTestCommand__ [globs...]', { hidden: true, isDefault: true }) .action((testFileGlobs) => { + const os = require('os'); + const RunTests = require('./RunTests'); + const { DependencyProvider } = require('./DependencyProvider.js'); + const options = program.opts(); const pathToElmBinary = getPathToElmBinary(options.compiler); const projectRootDir = getProjectRootDir('tests'); const processes = Math.max(1, os.cpus().length); + const dependencyProvider = new DependencyProvider(); RunTests.runTests( dependencyProvider, projectRootDir, + packageInfo.version, pathToElmBinary, testFileGlobs, processes,