typescript-assistant
Version:
Combines and integrates professional Typescript tools into your project
167 lines (156 loc) • 4.52 kB
text/typescript
import { Bus, EventType } from "../bus";
import { Git } from "../git";
import { Logger } from "../logger";
import { Task, TaskRunner } from "../taskrunner";
export interface NYC {
start(
triggers: EventType[],
withCoverage: boolean,
config?: string,
testsGlob?: string
): void;
stop(): void;
run(
withCoverage?: boolean,
config?: string,
testsGlob?: string
): Promise<boolean>;
}
function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function createNyc(dependencies: {
taskRunner: TaskRunner;
logger: Logger;
bus: Bus;
git: Git;
}): NYC {
const { taskRunner, logger, bus, git } = dependencies;
let runningTask: Task | undefined;
let coolingDown: Promise<void> | undefined;
async function startNyc(
withCoverage = true,
config?: string,
testsGlob = "test/**/*-tests.ts*"
): Promise<boolean> {
let hasFailingTest = false;
let myCoolingDown = delay(100);
coolingDown = myCoolingDown;
await myCoolingDown;
if (coolingDown !== myCoolingDown) {
return false;
}
if (runningTask) {
logger.log("nyc", "Aborting previous nyc run");
runningTask.kill();
runningTask = undefined;
} else {
bus.report({ tool: "test", status: "busy" });
bus.report({ tool: "coverage", status: "busy" });
}
let lastLineWasNotOk = false;
function handleOutput(line: string) {
if (task === runningTask) {
let notOk = /^not ok \d+ (.*)/.exec(line);
let ok = /^ok \d+ (.*)/.exec(line);
if (notOk) {
lastLineWasNotOk = true;
logger.log("nyc", `FAILED: ${notOk[1]}`);
hasFailingTest = true;
} else if (ok) {
lastLineWasNotOk = false;
} else if (lastLineWasNotOk) {
logger.log("nyc", line);
}
}
return true;
}
function handleError(line: string) {
if (task === runningTask && !line.startsWith("ERROR: Coverage for")) {
logger.error("nyc", line);
}
return true;
}
if (withCoverage) {
runningTask = taskRunner.runTask(
"./node_modules/.bin/nyc",
[
config ? `--nycrc-path ${config}` : "",
"--check-coverage",
"-- mocha --require ts-node/register/transpile-only --exit --reporter tap",
`"${testsGlob}"`,
]
.join(" ")
.trim()
.split(" "),
{
name: "nyc",
logger,
handleOutput,
handleError,
}
);
} else {
logger.log("nyc", "running tests without coverage");
runningTask = taskRunner.runTask(
"./node_modules/.bin/mocha",
[
"--require ts-node/register/transpile-only --exit --reporter tap",
`"${testsGlob}"`,
]
.join(" ")
.split(" "),
{
name: "nyc",
logger,
handleOutput,
handleError,
}
);
}
let task = runningTask;
return runningTask.result
.then(() => {
if (task === runningTask) {
runningTask = undefined;
logger.log("nyc", withCoverage ? "code coverage OK" : "tests ok");
bus.report({ tool: "test", status: "ready", errors: 0 });
bus.report({ tool: "coverage", status: "ready", errors: 0 });
}
return true;
})
.catch(async () => {
if (task === runningTask) {
runningTask = undefined;
logger.log("nyc", "code coverage FAILED");
bus.report({
tool: "test",
status: "ready",
errors: hasFailingTest ? 1 : 0,
});
bus.report({ tool: "coverage", status: "ready", errors: 1 });
}
let isOnMasterOrRelease = await git.isOnMasterOrReleaseBranch();
return !isOnMasterOrRelease && !hasFailingTest;
});
}
let callback: (() => Promise<boolean>) | undefined;
return {
run(withCoverage?: boolean, config?: string, testsGlob?: string) {
return startNyc(withCoverage, config, testsGlob).catch(() => false);
},
start(
triggers: EventType[],
withCoverage,
config?: string,
testsGlob?: string
) {
callback = () => startNyc(withCoverage, config, testsGlob);
bus.registerAll(triggers, callback as () => void);
callback().catch(() => false);
},
stop() {
bus.unregister(callback as () => void);
},
};
}