UNPKG

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
"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