archunit
Version:
ArchUnit TypeScript is an architecture testing library, to specify and assert architecture rules in your TypeScript app
376 lines (365 loc) • 16.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const metrics_1 = require("../../src/metrics");
const path_1 = __importDefault(require("path"));
const calculation_1 = require("../../src/metrics/calculation");
const ts = __importStar(require("typescript"));
describe('Count Metrics', () => {
describe('MethodCountMetric', () => {
it('should count methods correctly', () => {
const metric = new calculation_1.MethodCountMetric();
const classInfo = {
name: 'TestClass',
filePath: '/test/TestClass.ts',
methods: [
{ name: 'method1', accessedFields: [] },
{ name: 'method2', accessedFields: [] },
{ name: 'method3', accessedFields: [] },
],
fields: [],
};
const result = metric.calculate(classInfo);
expect(result).toBe(3);
});
it('should return 0 for class with no methods', () => {
const metric = new calculation_1.MethodCountMetric();
const classInfo = {
name: 'EmptyClass',
filePath: '/test/EmptyClass.ts',
methods: [],
fields: [],
};
const result = metric.calculate(classInfo);
expect(result).toBe(0);
});
it('should have correct name and description', () => {
const metric = new calculation_1.MethodCountMetric();
expect(metric.name).toBe('MethodCount');
expect(metric.description).toBe('Counts the number of methods in a class');
});
});
describe('FieldCountMetric', () => {
it('should count fields correctly', () => {
const metric = new calculation_1.FieldCountMetric();
const classInfo = {
name: 'TestClass',
filePath: '/test/TestClass.ts',
methods: [],
fields: [
{ name: 'field1', accessedBy: [] },
{ name: 'field2', accessedBy: [] },
],
};
const result = metric.calculate(classInfo);
expect(result).toBe(2);
});
it('should return 0 for class with no fields', () => {
const metric = new calculation_1.FieldCountMetric();
const classInfo = {
name: 'EmptyClass',
filePath: '/test/EmptyClass.ts',
methods: [],
fields: [],
};
const result = metric.calculate(classInfo);
expect(result).toBe(0);
});
it('should have correct name and description', () => {
const metric = new calculation_1.FieldCountMetric();
expect(metric.name).toBe('FieldCount');
expect(metric.description).toBe('Counts the number of fields/properties in a class');
});
});
describe('LinesOfCodeMetric', () => {
it('should count non-empty lines correctly', () => {
const metric = new calculation_1.LinesOfCodeMetric();
// Mock TypeScript SourceFile
const mockSourceFile = {
getFullText: () => `
// This is a comment
export class TestClass {
private field1: string;
public method1(): void {
console.log('test');
}
}
`,
};
const result = metric.calculateFromFile(mockSourceFile);
// Should count: export class..., private field1..., public method1()..., console.log...
// Should ignore: empty lines and comments
expect(result).toBeGreaterThan(0);
});
it('should ignore empty lines and comments', () => {
const metric = new calculation_1.LinesOfCodeMetric();
const mockSourceFile = {
getFullText: () => `
// Comment line 1
/* Comment line 2 */
/**
* Multi-line comment
*/
const variable = 'test';
`,
};
const result = metric.calculateFromFile(mockSourceFile);
expect(result).toBe(1); // Only "const variable = 'test';" should be counted
});
it('should have correct name and description', () => {
const metric = new calculation_1.LinesOfCodeMetric();
expect(metric.name).toBe('LinesOfCode');
expect(metric.description).toBe('Counts the total lines of code in a file (excluding empty lines and comments)');
});
});
describe('StatementCountMetric', () => {
it('should count statements correctly', () => {
const metric = new calculation_1.StatementCountMetric();
// Create a minimal TypeScript source file for testing
const sourceCode = `
const variable = 'test';
if (true) {
console.log('hello');
}
for (let i = 0; i < 10; i++) {
break;
}
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBeGreaterThan(0);
});
it('should have correct name and description', () => {
const metric = new calculation_1.StatementCountMetric();
expect(metric.name).toBe('StatementCount');
expect(metric.description).toBe('Counts the total number of statements in a file');
});
});
describe('ImportCountMetric', () => {
it('should count import statements correctly', () => {
const metric = new calculation_1.ImportCountMetric();
const sourceCode = `
import { Component } from 'react';
import * as fs from 'fs';
import './styles.css';
const variable = 'test';
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBe(3);
});
it('should return 0 when no imports', () => {
const metric = new calculation_1.ImportCountMetric();
const sourceCode = `
const variable = 'test';
console.log(variable);
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBe(0);
});
it('should have correct name and description', () => {
const metric = new calculation_1.ImportCountMetric();
expect(metric.name).toBe('ImportCount');
expect(metric.description).toBe('Counts the number of import statements in a file');
});
});
describe('ClassCountMetric', () => {
it('should count class declarations correctly', () => {
const metric = new calculation_1.ClassCountMetric();
const sourceCode = `
class TestClass1 {
test() {}
}
export class TestClass2 {
test() {}
}
abstract class TestClass3 {
abstract test(): void;
}
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBe(3);
});
it('should have correct name and description', () => {
const metric = new calculation_1.ClassCountMetric();
expect(metric.name).toBe('ClassCount');
expect(metric.description).toBe('Counts the number of classes in a file');
});
});
describe('InterfaceCountMetric', () => {
it('should count interface declarations correctly', () => {
const metric = new calculation_1.InterfaceCountMetric();
const sourceCode = `
interface Interface1 {
prop1: string;
}
export interface Interface2 {
prop2: number;
}
class TestClass {}
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBe(2);
});
it('should have correct name and description', () => {
const metric = new calculation_1.InterfaceCountMetric();
expect(metric.name).toBe('InterfaceCount');
expect(metric.description).toBe('Counts the number of interfaces in a file');
});
});
describe('FunctionCountMetric', () => {
it('should count function declarations correctly', () => {
const metric = new calculation_1.FunctionCountMetric();
const sourceCode = `
function testFunction1() {
return 'test1';
}
export function testFunction2() {
return 'test2';
}
const arrowFunction = () => 'test3';
class TestClass {
method() { // Should not be counted
return 'method';
}
}
`;
const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true);
const result = metric.calculateFromFile(sourceFile);
expect(result).toBeGreaterThanOrEqual(2); // At least the two function declarations
});
it('should have correct name and description', () => {
const metric = new calculation_1.FunctionCountMetric();
expect(metric.name).toBe('FunctionCount');
expect(metric.description).toBe('Counts the number of functions in a file (excluding class methods)');
});
});
describe('Fluent API Integration', () => {
it('should create count metrics builder', () => {
const builder = (0, metrics_1.metrics)().count();
expect(builder).toBeDefined();
expect(builder.methodCount).toBeDefined();
expect(builder.fieldCount).toBeDefined();
expect(builder.linesOfCode).toBeDefined();
});
it('should create method count threshold builder', () => {
const thresholdBuilder = (0, metrics_1.metrics)().count().methodCount();
expect(thresholdBuilder).toBeDefined();
expect(thresholdBuilder.shouldBeBelowOrEqual).toBeDefined();
expect(thresholdBuilder.shouldBeAbove).toBeDefined();
});
it('should create lines of code threshold builder', () => {
const thresholdBuilder = (0, metrics_1.metrics)().count().linesOfCode();
expect(thresholdBuilder).toBeDefined();
expect(thresholdBuilder.shouldBeBelowOrEqual).toBeDefined();
expect(thresholdBuilder.shouldBeAbove).toBeDefined();
});
it('should create all count metric builders', () => {
const builder = (0, metrics_1.metrics)().count();
expect(builder.methodCount).toBeDefined();
expect(builder.fieldCount).toBeDefined();
expect(builder.linesOfCode).toBeDefined();
expect(builder.statements).toBeDefined();
expect(builder.imports).toBeDefined();
expect(builder.classes).toBeDefined();
expect(builder.interfaces).toBeDefined();
expect(builder.functions).toBeDefined();
});
});
describe('Threshold Conditions', () => {
it('should create method count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().methodCount().shouldBeBelowOrEqual(10);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create lines of code condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().linesOfCode().shouldBeBelowOrEqual(100);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create field count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().fieldCount().shouldBeAbove(0);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create statement count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().statements().shouldBeBelow(50);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create import count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().imports().shouldBeBelowOrEqual(20);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create class count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().classes().shouldBeAboveOrEqual(1);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create interface count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().interfaces().shouldBe(2);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
it('should create function count condition with threshold', () => {
const condition = (0, metrics_1.metrics)().count().functions().shouldBeAbove(0);
expect(condition).toBeDefined();
expect(condition.check).toBeDefined();
});
});
describe('Threshold Validation', () => {
it('should validate method count violations correctly', async () => {
const condition = (0, metrics_1.metrics)().count().methodCount().shouldBeBelowOrEqual(10);
const violations = await condition.check();
// Since this uses real project files, we can only test that the check method works
expect(Array.isArray(violations)).toBe(true);
expect(typeof violations.length).toBe('number');
});
it('should not create violations when thresholds are met', async () => {
const mockProjectPath = path_1.default.join(__dirname, 'mock-project', 'tsconfig.json');
const methodCondition = (0, metrics_1.metrics)(mockProjectPath)
.count()
.methodCount()
.shouldBeBelowOrEqual(100);
const fieldCondition = (0, metrics_1.metrics)(mockProjectPath)
.count()
.fieldCount()
.shouldBeAboveOrEqual(0);
const methodViolations = await methodCondition.check();
const fieldViolations = await fieldCondition.check();
// Test that check methods return arrays
expect(Array.isArray(methodViolations)).toBe(true);
expect(Array.isArray(fieldViolations)).toBe(true);
});
});
});
//# sourceMappingURL=count-metrics.spec.js.map