Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(init-config): makes stderr, stdout configurable - so we can easier test their uses #897

Merged
merged 2 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/cli/init-config/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,40 @@ function manifestIsUpdatable(pNormalizedInitConfig) {
/**
* @param {boolean|import("./types").OneShotConfigIDType} pInit
* @param {string=} pConfigFileName
* @param {{stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream}=} pStreams
*/
export default function initConfig(pInit, pConfigFileName) {
export default function initConfig(pInit, pConfigFileName, pStreams) {
const lStreams = {
stdout: process.stdout,
stderr: process.stderr,
...pStreams,
};
/* c8 ignore start */
if (pInit === true) {
getUserInput()
.then(normalizeInitOptions)
.then(buildConfig)
.then(writeConfig)
.catch((pError) => {
process.stderr.write(`\n ERROR: ${pError.message}\n`);
lStreams.stderr.write(`\n ERROR: ${pError.message}\n`);
});
/* c8 ignore stop */
} else if (pInit !== false) {
const lNormalizedInitConfig = normalizeInitOptions(getOneShotConfig(pInit));
const lConfigFileName = pConfigFileName || getDefaultConfigFileName();

if (!fileExists(lConfigFileName)) {
writeConfig(buildConfig(lNormalizedInitConfig), lConfigFileName);
writeConfig(
buildConfig(lNormalizedInitConfig),
lConfigFileName,
lStreams.stdout,
);
}

if (manifestIsUpdatable(lNormalizedInitConfig)) {
writeRunScriptsToManifest(lNormalizedInitConfig);
writeRunScriptsToManifest(lNormalizedInitConfig, {
outStream: lStreams.stdout,
});
}
}
}
12 changes: 7 additions & 5 deletions src/cli/init-config/write-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
* @returns {void} Nothing
* @param {string} pConfig - dependency-cruiser configuration
* @param {import("fs").PathOrFileDescriptor} pFileName - name of the file to write to
* @param {NodeJS.WritableStream} pOutStream - the stream to write user feedback to
* @throws {Error} An error object with the root cause of the problem
* as a description:
* - file already exists
Expand All @@ -20,22 +21,23 @@ import {
*/
export default function writeConfig(
pConfig,
pFileName = getDefaultConfigFileName()
pFileName = getDefaultConfigFileName(),
pOutStream = process.stdout,
) {
if (fileExists(pFileName)) {
throw new Error(`A '${pFileName}' already exists here - leaving it be.\n`);
} else {
try {
writeFileSync(pFileName, pConfig);
process.stdout.write(
pOutStream.write(
`\n ${chalk.green(
figures.tick
)} Successfully created '${pFileName}'\n\n`
figures.tick,
)} Successfully created '${pFileName}'\n\n`,
);
/* c8 ignore start */
} catch (pError) {
throw new Error(
`ERROR: Writing to '${pFileName}' didn't work. ${pError}\n`
`ERROR: Writing to '${pFileName}' didn't work. ${pError}\n`,
);
}
/* c8 ignore stop */
Expand Down
25 changes: 17 additions & 8 deletions src/cli/init-config/write-run-scripts-to-manifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,30 @@ function getSuccessMessage(pDestinationManifestFileName) {
);
}

export function writeRunScriptsToManifest(
pNormalizedInitOptions,
pManifest = readManifest(),
pDestinationManifestFileName = PACKAGE_MANIFEST,
) {
/**
*
* @param {any} pNormalizedInitOptions
* @param {{manifest?: string, destinationManifestFileName?: string, outStream?: NodeJS.WritableStream}} pOptions
*/
export function writeRunScriptsToManifest(pNormalizedInitOptions, pOptions) {
const lOptions = {
manifest: readManifest(),
destinationManifestFileName: PACKAGE_MANIFEST,
outStream: process.stdout,
...pOptions,
};
const lUpdatedManifest = addRunScriptsToManifest(
pManifest,
lOptions.manifest,
compileRunScripts(pNormalizedInitOptions),
);

writeFileSync(
pDestinationManifestFileName,
lOptions.destinationManifestFileName,
JSON.stringify(lUpdatedManifest, null, " "),
"utf8",
);

process.stdout.write(getSuccessMessage(pDestinationManifestFileName));
lOptions.outStream.write(
getSuccessMessage(lOptions.destinationManifestFileName),
);
}
54 changes: 48 additions & 6 deletions test/cli/init-config/index.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { join } from "node:path";
import { deepEqual, equal } from "node:assert/strict";
import Ajv from "ajv";
import deleteDammit from "../delete-dammit.utl.cjs";
import {
UnCalledWritableTestStream,
WritableTestStream,
} from "./writable-test-stream.utl.mjs";
import configurationSchema from "#configuration-schema";
import initConfig from "#cli/init-config/index.mjs";

Expand All @@ -12,20 +16,27 @@ const RULES_FILE_JS = ".dependency-cruiser.js";

describe("[I] cli/init-config/index", () => {
const WORKINGDIR = process.cwd();
const lErrorStream = new UnCalledWritableTestStream();

afterEach("tear down", () => {
process.chdir(WORKINGDIR);
});

it("init creates a self-contained js rules file", async () => {
const lOutStream = new WritableTestStream(
/Successfully created '[.]dependency-cruiser[.]js'/,
);
process.chdir("test/cli/__fixtures__/init-config/no-config-files-exist");
const lConfigResultFileName = `./${join(
"../__fixtures__/init-config/no-config-files-exist",
RULES_FILE_JS,
)}`;

try {
initConfig("notyes");
initConfig("notyes", null, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand All @@ -36,6 +47,9 @@ describe("[I] cli/init-config/index", () => {
});

it("init yes creates a self-contained js rules file", async () => {
const lOutStream = new WritableTestStream(
/Successfully created '[.]dependency-cruiser-self-contained[.]js'/,
);
process.chdir("test/cli/__fixtures__/init-config/no-config-files-exist");
const lConfig = ".dependency-cruiser-self-contained.js";
const lConfigResultFileName = `./${join(
Expand All @@ -44,7 +58,10 @@ describe("[I] cli/init-config/index", () => {
)}`;

try {
initConfig("yes", lConfig);
initConfig("yes", lConfig, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand All @@ -56,14 +73,20 @@ describe("[I] cli/init-config/index", () => {
});

it("init yes in a ts project creates a self-contained js rules file with typescript things flipped to yes", async () => {
const lOutStream = new WritableTestStream(
/Successfully created '[.]dependency-cruiser[.]js'/,
);
process.chdir("test/cli/__fixtures__/init-config/ts-config-exists");
const lConfigResultFileName = `./${join(
"../__fixtures__/init-config/ts-config-exists",
RULES_FILE_JS,
)}`;

try {
initConfig("yes");
initConfig("yes", null, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand All @@ -78,14 +101,21 @@ describe("[I] cli/init-config/index", () => {
});

it("init yes in a webpack project creates a self-contained js rules file with webpack things flipped to yes", async () => {
const lOutStream = new WritableTestStream(
/Successfully created '[.]dependency-cruiser[.]js'/,
);

process.chdir("test/cli/__fixtures__/init-config/webpack-config-exists");
const lConfigResultFileName = `./${join(
"../__fixtures__/init-config/webpack-config-exists",
RULES_FILE_JS,
)}`;

try {
initConfig("yes");
initConfig("yes", null, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand All @@ -100,6 +130,9 @@ describe("[I] cli/init-config/index", () => {
});

it("init experimental-scripts creates a .dependency-cruiser config + updates package.json with scripts", async () => {
const lOutStream = new WritableTestStream(
/(Successfully created '.dependency-cruiser.js'|Run scripts added to '[.]\/package.json':)/,
);
process.chdir("test/cli/init-config/__fixtures__/update-manifest");

const lConfigResultFileName = `./${join(
Expand All @@ -111,7 +144,10 @@ describe("[I] cli/init-config/index", () => {
writeFileSync(lManifestFilename, "{}");

try {
initConfig("experimental-scripts");
initConfig("experimental-scripts", null, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand All @@ -125,6 +161,9 @@ describe("[I] cli/init-config/index", () => {
});

it("init experimental-scripts updates package.json with scripts, and leaves an existing dc config alone", async () => {
const lOutStream = new WritableTestStream(
/Run scripts added to '[.]\/package.json':/,
);
process.chdir(
"test/cli/init-config/__fixtures__/update-manifest-dc-config-exists",
);
Expand All @@ -137,7 +176,10 @@ describe("[I] cli/init-config/index", () => {
writeFileSync(lManifestFilename, "{}");

try {
initConfig("experimental-scripts");
initConfig("experimental-scripts", null, {
stdout: lOutStream,
stderr: lErrorStream,
});
const lResult = await import(lConfigResultFileName);

ajv.validate(configurationSchema, lResult.default);
Expand Down
27 changes: 27 additions & 0 deletions test/cli/init-config/writable-test-stream.utl.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable max-classes-per-file */
import { match } from "node:assert";
import { Writable } from "node:stream";

export class WritableTestStream extends Writable {
expected = /^$/;

/**
* @param {RegExp=} pExpected
*/
constructor(pExpected) {
super();
if (pExpected) {
this.expected = pExpected;
}
}
write(pChunk) {
match(pChunk, this.expected);
return true;
}
}

export class UnCalledWritableTestStream extends Writable {
write(pChunk) {
throw new Error(`Unexpected write to stream: ${pChunk}`);
}
}
5 changes: 5 additions & 0 deletions test/cli/init-config/write-config.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ok, equal } from "node:assert/strict";
import { writeFileSync, readFileSync } from "node:fs";
import { join } from "node:path";
import deleteDammit from "../delete-dammit.utl.cjs";
import { WritableTestStream } from "./writable-test-stream.utl.mjs";
import writeConfig from "#cli/init-config/write-config.mjs";

const RULES_FILE_JS = ".dependency-cruiser.js";
Expand All @@ -14,6 +15,9 @@ describe("[U] cli/init-config/write-config", () => {
});

it("writes if there's no file there yet", async () => {
const lOutStream = new WritableTestStream(
/Successfully created 'depcruise[.]config[.]js'/,
);
const lEmptyDirectory =
"test/cli/__fixtures__/init-config/no-config-files-exist";
const lCustomConfigFileName = "depcruise.config.js";
Expand All @@ -30,6 +34,7 @@ describe("[U] cli/init-config/write-config", () => {
wim: { zus: "jet heide", does: "hok schapen" },
}`,
lCustomConfigFileName,
lOutStream,
);

const lResult = await import(lConfigResultFileName);
Expand Down