Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions lib/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand All @@ -18,6 +16,5 @@ function isMachineReadable(report /*: typeof Report */) /*: boolean */ {

module.exports = {
Report,
all,
isMachineReadable,
};
65 changes: 37 additions & 28 deletions lib/RunTests.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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'
Expand All @@ -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/*:: <T> */(
from /*: Array<T> */,
to /*: Array<T> */
Expand All @@ -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];
Expand All @@ -123,6 +127,7 @@ function watcherEventMessage(queue /*: typeof Queue */) /*: string */ {
function runTests(
dependencyProvider /*: DependencyProvider */,
projectRootDir /*: string */,
packageVersion /*: string */,
pathToElmBinary /*: string */,
testFileGlobs /*: Array<string> */,
processes /*: number */,
Expand All @@ -146,7 +151,10 @@ function runTests(
let currentRun /*: Promise<void> | 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<number> */ {
try {
Expand All @@ -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));
}
}

Expand All @@ -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);

Expand Down Expand Up @@ -241,7 +249,7 @@ function runTests(
progressLogger.newLine();

return await Supervisor.run(
packageInfo.version,
packageVersion,
pipeFilename,
report,
processes,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion lib/Solve.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @flow

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const ElmJson = require('./ElmJson');
Expand All @@ -13,6 +12,7 @@ void Project;
void DependencyProvider;

function sha256(string) {
const crypto = require('crypto');
return crypto.createHash('sha256').update(string).digest('hex');
}

Expand Down
61 changes: 32 additions & 29 deletions lib/elm-test.js
Original file line number Diff line number Diff line change
@@ -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 */) =>
Expand All @@ -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 */ {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -99,8 +89,6 @@ elm-test "src/**/*Tests.elm"
`.trim();

function main() {
const dependencyProvider = new DependencyProvider();

process.title = 'elm-test';

program
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'),
Expand All @@ -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);
});
Expand All @@ -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');
Expand All @@ -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(
Expand All @@ -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,
Expand Down
Loading