code-complexity
Version:
Measure the churn/complexity score. Higher values mean hotspots where refactorings should happen.
469 lines (426 loc) • 11.9 kB
text/typescript
import { sep } from "node:path";
import { tmpdir } from "node:os";
import { expect } from "chai";
import { Options } from "../../src/lib/types";
import Statistics from "../../src/lib";
import TestRepositoryFixture from "../fixtures/test-repository.fixture";
describe("Statistics", () => {
const defaultOptions: Options = {
directory: tmpdir() + sep + TestRepositoryFixture.testRepositoryName,
target: tmpdir() + sep + TestRepositoryFixture.testRepositoryName,
format: "json",
filter: [],
limit: 3,
since: undefined,
sort: "score",
};
context("options.limit", () => {
it("returns the appropriate number of elements", async () => {
// Given
const options: Options = { ...defaultOptions, limit: 3 };
new TestRepositoryFixture()
.addFile({ name: "a.js" })
.addFile({ name: "b.js" })
.addFile({ name: "c.js" })
.addFile({ name: "d.js" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.have.length(3);
});
});
context("options.filter", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
filter: ["*.js"],
};
new TestRepositoryFixture()
.addFile({ name: "a.js" })
.addFile({ name: "b.md" })
.addFile({ name: "c.js" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 1,
path: "a.js",
score: 1,
},
{
churn: 1,
complexity: 1,
path: "c.js",
score: 1,
},
]);
});
});
context("options.since", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = { ...defaultOptions, since: "2010-01-01" };
new TestRepositoryFixture()
.addFile({ name: "a.js", date: "2000-01-01T00:00:00" })
.addFile({ name: "b.js", date: "2020-01-01T00:00:00" })
.addFile({ name: "c.js", date: "2020-01-01T00:00:00" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 1,
path: "b.js",
score: 1,
},
{
churn: 1,
complexity: 1,
path: "c.js",
score: 1,
},
]);
});
});
context("options.until", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = { ...defaultOptions, until: "2010-01-01" };
new TestRepositoryFixture()
.addFile({ name: "a.js", date: "2000-01-01T00:00:00" })
.addFile({ name: "b.js", date: "2020-01-01T00:00:00" })
.addFile({ name: "c.js", date: "2020-01-01T00:00:00" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 1,
path: "a.js",
score: 1,
},
]);
});
});
context("options.sort=score", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = { ...defaultOptions, sort: "score" };
new TestRepositoryFixture()
.addFile({ name: "a.js", lines: 1, commits: 4 })
.addFile({ name: "b.js", lines: 3, commits: 3 })
.addFile({ name: "c.js", lines: 3, commits: 2 })
.addFile({ name: "d.js", lines: 4, commits: 2 })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 3,
complexity: 3,
path: "b.js",
score: 9,
},
{
churn: 2,
complexity: 4,
path: "d.js",
score: 8,
},
{
churn: 2,
complexity: 3,
path: "c.js",
score: 6,
},
]);
});
});
context("options.sort=complexity", () => {
context("when using cyclomatic strategy", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
sort: "complexity",
complexityStrategy: "cyclomatic",
};
new TestRepositoryFixture()
.addFile({
name: "a.ts",
content: "if (true) if (true) console.log();",
})
.addFile({
name: "b.js",
content: "if (true) if (true) console.log();",
})
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 3,
path: "a.ts",
score: 3,
},
{
churn: 1,
complexity: 3,
path: "b.js",
score: 3,
},
]);
});
});
context("when using halstead strategy", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
sort: "complexity",
complexityStrategy: "halstead",
};
new TestRepositoryFixture()
.addFile({
name: "a.ts",
content: "if (true) if (true) console.log();",
})
.addFile({
name: "b.js",
content: "if (true) if (true) console.log();",
})
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 25.26619429851844,
path: "a.ts",
score: 25.26619429851844,
},
{
churn: 1,
complexity: 25.26619429851844,
path: "b.js",
score: 25.26619429851844,
},
]);
});
});
context("when using sloc strategy", () => {
context("when analyzed file is supported by node-sloc", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
complexityStrategy: "sloc",
sort: "complexity",
};
new TestRepositoryFixture()
.addFile({ name: "a.js", lines: 8 })
.addFile({ name: "b.js", lines: 6 })
.addFile({ name: "c.js", lines: 2 })
.addFile({ name: "d.js", lines: 4 })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 8,
path: "a.js",
score: 8,
},
{
churn: 1,
complexity: 6,
path: "b.js",
score: 6,
},
{
churn: 1,
complexity: 4,
path: "d.js",
score: 4,
},
]);
});
});
context("when analyzed file is not supported by node-sloc", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
complexityStrategy: "sloc",
sort: "complexity",
};
new TestRepositoryFixture()
.addFile({ name: "a.txt", lines: 8 })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 7,
path: "a.txt",
score: 7,
},
]);
});
});
});
});
context("options.sort=churn", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = { ...defaultOptions, sort: "churn" };
new TestRepositoryFixture()
.addFile({ name: "a.js", commits: 7 })
.addFile({ name: "b.js", commits: 3 })
.addFile({ name: "c.js", commits: 5 })
.addFile({ name: "d.js", commits: 2 })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 7,
complexity: 1,
path: "a.js",
score: 7,
},
{
churn: 5,
complexity: 1,
path: "c.js",
score: 5,
},
{
churn: 3,
complexity: 1,
path: "b.js",
score: 3,
},
]);
});
});
context("options.sort=file", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = { ...defaultOptions, sort: "file" };
new TestRepositoryFixture()
.addFile({ name: "d.js", lines: 1, commits: 4 })
.addFile({ name: "a.js", lines: 2, commits: 3 })
.addFile({ name: "c.js", lines: 3, commits: 2 })
.addFile({ name: "b.js", lines: 4, commits: 1 })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 3,
complexity: 2,
path: "a.js",
score: 6,
},
{
churn: 1,
complexity: 4,
path: "b.js",
score: 4,
},
{
churn: 2,
complexity: 3,
path: "c.js",
score: 6,
},
]);
});
});
context("options.directories=true", () => {
it("returns the appropriate elements", async () => {
// Given
const options: Options = {
...defaultOptions,
limit: 4,
directories: true,
};
new TestRepositoryFixture()
.addFile({ name: "test/a.js" })
.addFile({ name: "test/foo/b.js" })
.addFile({ name: "test/foo/c.js" })
.addFile({ name: "test/bar/qux/d.js" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
path: "test",
churn: 4,
complexity: 4,
score: 16,
},
{
path: "test/foo",
churn: 2,
complexity: 2,
score: 4,
},
{
path: "test/bar",
churn: 1,
complexity: 1,
score: 1,
},
{
path: "test/bar/qux",
churn: 1,
complexity: 1,
score: 1,
},
]);
});
});
context("when file no longer exists", () => {
it("it is ignored", async () => {
// Given
const options: Options = { ...defaultOptions, sort: "file" };
new TestRepositoryFixture()
.addFile({ name: "a.js", removed: true })
.addFile({ name: "b.ts" })
.writeOnDisk();
// When
const result = (await Statistics.compute(options)).list();
// Then
expect(result).to.deep.equal([
{
churn: 1,
complexity: 1,
path: "b.ts",
score: 1,
},
]);
});
});
});