дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
Only the repository containing the project root was known, so nested clones and submodules (or a folder of side-by-side service repositories) showed no changes and could not be committed or pushed. The project now has IDEA's VCS roots: the enclosing repository plus those found below the project (scanned in the background, rescanned when a .git appears or goes). Status covers all of them; path-based commands go to the file's repository and repository-scoped ones take the selected repository. The Commit view groups changes by repository and commits each one with included changes; the branch widget names the selected repository and switches between them; Update Project and Fetch run in every repository; Push lists every repository with outgoing commits; a repository that stops in a merge or rebase is selected so Continue / Abort act on it. Closes #12
94 рядки
4.1 KiB
TypeScript
94 рядки
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { messageProblems, wrapAtMargin } from "../CommitView";
|
|
import { linkifyIssues } from "../issueLinks";
|
|
import { defaultPushChecked, isProtectedBranch, isPushRejected, pushCandidates, type RepoPush } from "../remoteUi";
|
|
import { attentionRepo } from "../store";
|
|
import { statusDecorations } from "../model";
|
|
|
|
const cfg = { blankLineAfterSubject: true, limitSubject: true, subjectLimit: 10, limitBody: true, bodyLimit: 8 };
|
|
|
|
describe("commit message inspections", () => {
|
|
it("reports subject length, missing blank line and long body lines", () => {
|
|
expect(messageProblems("short", cfg)).toEqual([]);
|
|
expect(messageProblems("a subject that is long\nbody", cfg)).toEqual([
|
|
"Subject line is longer than 10 characters",
|
|
"Missing blank line between subject and body",
|
|
]);
|
|
expect(messageProblems("ok\n\nthis body line is long", cfg)).toEqual(["Body line 3 is longer than 8 characters"]);
|
|
expect(messageProblems("x".repeat(20), { ...cfg, limitSubject: false })).toEqual([]);
|
|
});
|
|
|
|
it("wraps body lines at the last space before the margin", () => {
|
|
const text = "subject\n\nhello world again";
|
|
expect(wrapAtMargin(text, text.length, 12)).toEqual({ text: "subject\n\nhello world\nagain", caret: text.length });
|
|
expect(wrapAtMargin("a very long subject line", 5, 5)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("issue navigation", () => {
|
|
it("links every match with $0 / $n substitution", () => {
|
|
const links = [
|
|
{ pattern: "[A-Z]+-\\d+", link: "https://jira/browse/$0" },
|
|
{ pattern: "#(\\d+)", link: "https://gh/issues/$1" },
|
|
{ pattern: "(", link: "broken" },
|
|
];
|
|
expect(linkifyIssues("Fix OPENIDEA-12 and #7.", links)).toEqual([
|
|
"Fix ",
|
|
{ text: "OPENIDEA-12", url: "https://jira/browse/OPENIDEA-12" },
|
|
" and ",
|
|
{ text: "#7", url: "https://gh/issues/7" },
|
|
".",
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("git settings helpers", () => {
|
|
it("matches protected branch globs", () => {
|
|
expect(isProtectedBranch("main", "master; main")).toBe(true);
|
|
expect(isProtectedBranch("release/1.2", "release/*")).toBe(true);
|
|
expect(isProtectedBranch("feature", "master, main")).toBe(false);
|
|
});
|
|
|
|
it("recognises rejected pushes", () => {
|
|
expect(isPushRejected(" ! [rejected] main -> main (fetch first)")).toBe(true);
|
|
expect(isPushRejected("Permission denied (publickey)")).toBe(false);
|
|
});
|
|
|
|
it("propagates folder colours only when enabled", () => {
|
|
const status = [{ path: "/p/src/a.ts", relPath: "src/a.ts", staged: null, unstaged: "modified" as const, renamedFrom: null }];
|
|
expect(statusDecorations(status).get("/p/src/a.ts")?.propagate).toBe(true);
|
|
expect(statusDecorations(status, false).get("/p/src/a.ts")?.propagate).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("several repositories", () => {
|
|
it("selects a repository that just stopped in an operation", () => {
|
|
expect(attentionRepo("/p", {}, { "/p/lib": "merge" })).toBe("/p/lib");
|
|
expect(attentionRepo("/p", { "/p/lib": "merge" }, { "/p/lib": "merge" })).toBeNull();
|
|
expect(attentionRepo("/p", {}, { "/p": "rebase", "/p/lib": "merge" })).toBeNull();
|
|
expect(attentionRepo("/p", {}, {})).toBeNull();
|
|
});
|
|
|
|
it("lists repositories with something to push", () => {
|
|
const branch = (head: string | null, upstream: string | null) => ({
|
|
head,
|
|
headCommit: "c1",
|
|
upstream,
|
|
ahead: 0,
|
|
behind: 0,
|
|
operation: null,
|
|
});
|
|
const commit = { id: "c1", shortId: "c1" } as RepoPush["commits"][number];
|
|
const repos: RepoPush[] = [
|
|
{ root: "/p", branch: branch("main", "origin/main"), commits: [commit] },
|
|
{ root: "/p/a", branch: branch("main", "origin/main"), commits: [] },
|
|
{ root: "/p/b", branch: branch("feature", null), commits: [commit] },
|
|
{ root: "/p/c", branch: branch(null, null), commits: [] },
|
|
];
|
|
const candidates = pushCandidates(repos);
|
|
expect(candidates.map((r) => r.root)).toEqual(["/p", "/p/b"]);
|
|
expect([...defaultPushChecked(candidates, "/p")]).toEqual(["/p"]);
|
|
expect([...defaultPushChecked(candidates, "/p/b")]).toEqual(["/p", "/p/b"]);
|
|
});
|
|
});
|