Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 1 addition & 1 deletion src/cli/install-commitlint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const packageJson = new PackageJson();
export const installCommitLint = async () => {
await installHusky();

await packageJson.install("@commitlint/cli", { version: "20", dev: true });
await packageJson.install("@commitlint/cli", { version: "^20", dev: true });

await writeFile(
path.join(root, ".husky/commit-msg"),
Expand Down
2 changes: 1 addition & 1 deletion src/cli/templates/node-setup-ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const nodeSetupCi = ({
uses: actions/checkout@v6
with:
fetch-depth: 0
${manager.name === "pnpm" ? pnpmSetupCi({ pnpmVersion }) : ""}
${manager.name === "pnpm" ? pnpmSetupCi({ pnpmVersion }) : ""}
- name: Setup node
Comment thread
kguzek marked this conversation as resolved.
uses: actions/setup-node@v6
with:
Expand Down
163 changes: 163 additions & 0 deletions tests/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,76 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { getCurrentPackageManager } from "./utils/package-manager";
import { TestEnvironment } from "./utils/test-environment";

const expectGeneratedConfigToBeFormatted = async ({
env,
appPath,
expectedGeneratedFiles,
}: {
env: TestEnvironment;
appPath: string;
expectedGeneratedFiles: string[];
}) => {
const missingFiles = expectedGeneratedFiles.filter(
(filePath) => !env.fileExists(appPath, filePath),
);
expect(
missingFiles,
`Missing generated files:\n${missingFiles.join("\n")}`,
).toEqual([]);

const generatedFilesThatExist = expectedGeneratedFiles.filter((filePath) =>
env.fileExists(appPath, filePath),
);
const prettierCheckResult = await env.runPrettierCheckIfInstalled(
appPath,
generatedFilesThatExist,
Comment thread
kguzek marked this conversation as resolved.
);
Comment thread
kguzek marked this conversation as resolved.
Comment thread
kguzek marked this conversation as resolved.
expect(prettierCheckResult.skipped, prettierCheckResult.output).toBe(false);
expect(prettierCheckResult.success, prettierCheckResult.output).toBe(true);
};

const expectCommitlintHookToAllowMessage = async ({
env,
appPath,
message,
}: {
env: TestEnvironment;
appPath: string;
message: string;
}) => {
env.writeFile(
appPath,
"commitlint-hook-test.txt",
`commitlint hook test ${Date.now()}\n`,
);
const commitResult = await env.commitWithMessage(appPath, message);
expect(commitResult.success, commitResult.output).toBe(true);
};
Comment thread
kguzek marked this conversation as resolved.

const expectCommitlintHookToRejectMessage = async ({
env,
appPath,
message,
}: {
env: TestEnvironment;
appPath: string;
message: string;
}) => {
env.writeFile(
appPath,
"commitlint-hook-test-invalid.txt",
`commitlint hook invalid test ${Date.now()}\n`,
);
const commitResult = await env.commitWithMessage(appPath, message);
expect(commitResult.success, commitResult.output).toBe(false);
expect(
/subject-empty|type-empty|commit-msg script failed/i.test(
commitResult.output,
),
commitResult.output,
).toBe(true);
};

describe("Next.js Integration Tests", () => {
let testEnv: TestEnvironment;
const packageManager = getCurrentPackageManager();
Expand All @@ -27,6 +97,18 @@ describe("Next.js Integration Tests", () => {
const output = await env.runSolvroConfig(appPath, ["--all", "--force"]);
expect(output).toContain("Konfiguracja zakończona pomyślnie");

await expectGeneratedConfigToBeFormatted({
env,
appPath,
expectedGeneratedFiles: [
"eslint.config.js",
"package.json",
".github/workflows/ci.yml",
".github/dependabot.yml",
".commitlintrc.js",
],
});

const prettierResult = await env.runPrettier(appPath, true);
expect(prettierResult.success).toBe(true);

Expand Down Expand Up @@ -77,6 +159,18 @@ describe("NestJS Integration Tests", () => {
const output = await env.runSolvroConfig(appPath, ["--all", "--force"]);
expect(output).toContain("Konfiguracja zakończona pomyślnie");

await expectGeneratedConfigToBeFormatted({
env,
appPath,
expectedGeneratedFiles: [
"eslint.config.mjs",
"package.json",
".github/workflows/ci.yml",
".github/dependabot.yml",
".commitlintrc.js",
],
});

const prettierResult = await env.runPrettier(appPath, true);
expect(prettierResult.success).toBe(true);

Expand Down Expand Up @@ -127,6 +221,18 @@ describe("Vite Integration Tests", () => {
const output = await env.runSolvroConfig(appPath, ["--all", "--force"]);
expect(output).toContain("Konfiguracja zakończona pomyślnie");

await expectGeneratedConfigToBeFormatted({
env,
appPath,
expectedGeneratedFiles: [
"eslint.config.js",
"package.json",
".github/workflows/ci.yml",
".github/dependabot.yml",
".commitlintrc.js",
],
});

const prettierResult = await env.runPrettier(appPath, true);
expect(prettierResult.success).toBe(true);

Expand Down Expand Up @@ -154,3 +260,60 @@ describe("Vite Integration Tests", () => {
expect(packageJson).toContain('"prettier": "@solvro/config/prettier"');
});
});

describe("Commitlint Integration Tests", () => {
let testEnv: TestEnvironment;
const packageManager = getCurrentPackageManager();

beforeEach(async () => {
testEnv = new TestEnvironment("commitlint-integration", packageManager);
});

afterEach(() => {
testEnv?.cleanup();
});

test(`should allow valid commit message through husky commit-msg hook with ${packageManager.name}`, async () => {
const env = testEnv;
await env.setup();

const appPath = await env.createNextjsApp("commitlint-test-app");
await env.installSolvroConfig(appPath);
await env.initGitRepo(appPath);

const output = await env.runSolvroConfig(appPath, [
"--commitlint",
"--force",
]);
expect(output).toContain("Konfiguracja zakończona pomyślnie");

await expectGeneratedConfigToBeFormatted({
env,
appPath,
expectedGeneratedFiles: ["package.json", ".commitlintrc.js"],
});

const packageJson = JSON.parse(env.readFile(appPath, "package.json")) as {
scripts?: Record<string, string>;
};
packageJson.scripts = packageJson.scripts ?? {};
packageJson.scripts.test = packageJson.scripts.test ?? "true";
env.writeFile(
appPath,
"package.json",
JSON.stringify(packageJson, null, 2),
);
Comment thread
kguzek marked this conversation as resolved.

await expectCommitlintHookToAllowMessage({
env,
appPath,
message: "chore: test commit",
});

await expectCommitlintHookToRejectMessage({
env,
appPath,
message: "invalid commit message",
});
});
});
77 changes: 77 additions & 0 deletions tests/utils/test-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,83 @@ export class TestEnvironment {
}
}

async runPrettierCheckIfInstalled(
appPath: string,
paths: string[] = ["."],
): Promise<{ success: boolean; output: string; skipped: boolean }> {
const packageJson = JSON.parse(this.readFile(appPath, "package.json")) as {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};
const hasPrettier =
packageJson.dependencies?.prettier != null ||
packageJson.devDependencies?.prettier != null;

if (paths.length === 0) {
return {
success: true,
output: "Skipped prettier check: no files selected.",
skipped: true,
};
}

const prettierCommand: keyof PackageManagerConfig = hasPrettier
? "localExecute"
: "downloadExecute";

const prettierArgs = ["prettier", "--check", ...paths];

if (!hasPrettier && this.packageManager.name === "npm") {
prettierArgs.unshift("--yes");
}

try {
const { stdout, stderr } = await this.execute({
command: prettierCommand,
args: prettierArgs,
cwd: appPath,
label: hasPrettier ? "prettier-check" : "prettier-check-dlx",
Comment thread
kguzek marked this conversation as resolved.
});
Comment thread
kguzek marked this conversation as resolved.
return { success: true, output: stdout + stderr, skipped: false };
} catch (error: any) {
return {
success: false,
output: (error.stdout || "") + (error.stderr || ""),
skipped: false,
};
}
}

async commitWithMessage(
appPath: string,
message: string,
): Promise<{ success: boolean; output: string }> {
try {
const { stdout: addStdout, stderr: addStderr } = await execWithLogging(
"git",
["add", "."],
{ cwd: appPath },
"git-add-for-commit",
);
const { stdout: commitStdout, stderr: commitStderr } =
await execWithLogging(
"git",
["commit", "-m", message],
{ cwd: appPath },
"git-commit-with-message",
);
return {
success: true,
output: addStdout + addStderr + commitStdout + commitStderr,
};
} catch (error: any) {
return {
success: false,
output: (error.stdout || "") + (error.stderr || ""),
};
}
}

async buildNextjsApp(
appPath: string,
): Promise<{ success: boolean; output: string }> {
Expand Down
Loading