diff --git a/.opencode/module-graph.md b/.opencode/module-graph.md index 99cc195..18e90a2 100644 --- a/.opencode/module-graph.md +++ b/.opencode/module-graph.md @@ -5,7 +5,7 @@ ``` lib/ model/ ← data classes, enums, exceptions (zero internal deps) - data/ ← data access, serialization (depends on model/) + data/ ← data access, serialization, file I/O (depends on model/) analyzer/ ← analysis engine, rule checkers (depends on model/, data/) checker/ ← individual rule implementations (depends on model/) util/ ← shared utilities (zero internal deps) @@ -53,12 +53,24 @@ bin/ | Layer | Role | |---|---| | `model/` | Data classes, enums, custom exceptions. Zero knowledge of other layers. | -| `data/` | Config loading, JSON parsing, file I/O. Depends on `model/` for types. | +| `data/` | Config loading, JSON mapping, Dart source parsing, file I/O. Depends on `model/` for types and may use external packages (`yaml`, `analyzer`). | | `analyzer/` | Core logic: walks files, resolves imports, checks rules. Orchestrates the analysis. | | `analyzer/checker/` | Individual rule implementations. Each checker validates one rule type. | | `util/` | Shared helpers (string, file, path utilities). No knowledge of project domain. | | `bin/main.dart` | CLI entry point. Only calls the public API — never internal modules directly. | +## Data Layer Contents + +- `config_item_mapper.dart` — maps JSON objects to `EagleEyeConfigItem`. +- `config_keys.dart` — JSON key names (wire format). +- `dart_source_loader.dart` — reads Dart files and returns `CompilationUnit`. +- `json_converter.dart` — parses the configuration array. +- `eagle_eye_repository.dart` — exposes config, application name, and Dart paths. +- `file_helper.dart` — internal file-system operations. + +The model layer has no wire-format knowledge: `EagleEyeConfigItem.fromJson` +does not exist, and JSON keys live in `data/`. + ## Adding a New Feature 1. If a new domain type is needed → add to `model/` diff --git a/CHANGELOG.md b/CHANGELOG.md index d71168a..ae8c0c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Unreleased +- Internal refactor: move JSON mapping, configuration keys, and Dart source loading into `lib/data/`. `EagleEyeConfigItem` is now a pure model with no `fromJson` factory, and `EagleEyeRepository` returns Dart file paths instead of `File` objects. `FileHelper` no longer references `util/logger_helper.dart`. - Add a configuration validation step before analysis. EagleEye now rejects malformed rules and configurations where more than one rule matches the same file, instead of silently applying the first match. - Add a `--verbose` (`-v`) flag that prints every configuration, file, import, and pattern check performed by EagleEye. Add `--help` (`-h`) for usage information. diff --git a/lib/data/config_item_mapper.dart b/lib/data/config_item_mapper.dart new file mode 100644 index 0000000..d593eec --- /dev/null +++ b/lib/data/config_item_mapper.dart @@ -0,0 +1,78 @@ +import 'package:eagle_eye/data/config_keys.dart'; +import 'package:eagle_eye/model/eagle_eye_config_item.dart'; +import 'package:eagle_eye/model/exceptions/invalid_config_exception.dart'; + +/// Maps configuration JSON objects into [EagleEyeConfigItem] instances. +/// +/// This is a data-layer concern: the model stays free of wire-format +/// knowledge, while the mapper owns key names and type validation. +class ConfigItemMapper { + /// Maps a single JSON object into an [EagleEyeConfigItem]. + /// + /// The [ConfigKeys.nameEagleItemKey] field is optional and maps to `null` + /// when absent. Throws an [InvalidConfigException] when a required field is + /// missing or when a field has an unexpected type. + EagleEyeConfigItem map(Map json) { + final dependenciesAllowed = _readDependenciesAllowed(json); + final forbiddenDependencies = _readStringList( + json, + ConfigKeys.forbiddenDependenciesEagleItemKey, + ); + final exclusiveDependencies = _readStringList( + json, + ConfigKeys.exclusiveDependenciesEagleItemKey, + ); + final filePattern = _readFilePattern(json); + final name = _readName(json); + + return EagleEyeConfigItem( + dependenciesAllowed: dependenciesAllowed, + forbiddenDependencies: forbiddenDependencies, + exclusiveDependencies: exclusiveDependencies, + filePattern: filePattern, + name: name, + ); + } + + bool? _readDependenciesAllowed(Map json) { + final key = ConfigKeys.dependenciesAllowedEagleItemKey; + final value = json[key]; + if (value != null && value is! bool) { + throw InvalidConfigException('The "$key" value must be a boolean.'); + } + return value as bool?; + } + + List? _readStringList(Map json, String key) { + final value = json[key]; + if (value == null) { + return null; + } + if (value is! List || value.any((item) => item is! String)) { + throw InvalidConfigException( + 'The "$key" value must be a list of strings.', + ); + } + return List.from(value); + } + + String _readFilePattern(Map json) { + final key = ConfigKeys.filePatternEagleItemKey; + final value = json[key]; + if (value is! String || value.trim().isEmpty) { + throw InvalidConfigException( + 'The "$key" value is required and must be a non-empty string.', + ); + } + return value; + } + + String? _readName(Map json) { + final key = ConfigKeys.nameEagleItemKey; + final value = json[key]; + if (value != null && value is! String) { + throw InvalidConfigException('The "$key" value must be a string.'); + } + return value as String?; + } +} diff --git a/lib/model/config_keys.dart b/lib/data/config_keys.dart similarity index 100% rename from lib/model/config_keys.dart rename to lib/data/config_keys.dart diff --git a/lib/data/dart_source_loader.dart b/lib/data/dart_source_loader.dart new file mode 100644 index 0000000..7dc1e6d --- /dev/null +++ b/lib/data/dart_source_loader.dart @@ -0,0 +1,17 @@ +import 'package:analyzer/dart/analysis/features.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/ast/ast.dart'; + +/// Loads Dart source files from disk and parses them into ASTs. +/// +/// This is a data-layer concern: it owns reading source files and turning +/// them into [CompilationUnit] values for the analyzer. +class DartSourceLoader { + /// Parses the Dart file at [path] into a [CompilationUnit]. + CompilationUnit load(String path) { + return parseFile( + path: path, + featureSet: FeatureSet.latestLanguageVersion(), + ).unit; + } +} diff --git a/lib/data/eagle_eye_repository.dart b/lib/data/eagle_eye_repository.dart index e0f44fa..d326afd 100644 --- a/lib/data/eagle_eye_repository.dart +++ b/lib/data/eagle_eye_repository.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:eagle_eye/data/file_helper.dart'; import 'package:eagle_eye/data/json_converter.dart'; import 'package:eagle_eye/model/eagle_eye_config.dart'; @@ -21,8 +19,9 @@ abstract class EagleEyeRepository { /// Returns an [EagleEyeConfig] Future getAndCheckIfConfigFileExists(); - /// Returns all Dart files found within the project’s `lib/` directory. - List allDartFiles(); + /// Returns the paths of all Dart files found within the project’s `lib/` + /// directory. + List allDartPaths(); /// Returns the application’s name as defined in the project structure. String getApplicationName(); @@ -56,17 +55,15 @@ class EagleEyeRepositoryImpl extends EagleEyeRepository { }); @override - List allDartFiles() { - Directory libsDir = fileHelper.getAndCheckIfLibsDirectoryExists( - libsFolderName, - ); - return fileHelper.allDartFiles(libsDir); + List allDartPaths() { + final libsDir = fileHelper.getAndCheckIfLibsDirectoryExists(libsFolderName); + return fileHelper.allDartFiles(libsDir).map((file) => file.path).toList(); } @override Future getAndCheckIfConfigFileExists() async { - final String configFileContent = - await fileHelper.getAndCheckIfConfigFileExists(configFileName); + final String configFileContent = await fileHelper + .getAndCheckIfConfigFileExists(configFileName); return jsonConverter.convert(configFileContent); } diff --git a/lib/data/file_helper.dart b/lib/data/file_helper.dart index 52d75c7..b1af4bd 100644 --- a/lib/data/file_helper.dart +++ b/lib/data/file_helper.dart @@ -3,7 +3,6 @@ import 'package:eagle_eye/model/exceptions/application_name_not_found_exception. import 'package:eagle_eye/model/exceptions/config_file_not_found_exception.dart'; import 'package:eagle_eye/model/exceptions/empty_dart_file_list_exception.dart'; import 'package:eagle_eye/model/exceptions/library_folder_not_found_exception.dart'; -import 'package:eagle_eye/util/logger_helper.dart'; import 'package:yaml/yaml.dart'; /// Provides utility methods for interacting with project files and directories. @@ -17,10 +16,9 @@ import 'package:yaml/yaml.dart'; class FileHelper { // Checks if the specified configuration file exists and reads its contents. /// - /// If the file does not exist, logs an error message via [LoggerHelper]. + /// Throws a [ConfigFileNotFoundException] if the file does not exist. /// /// Returns the file content as a [String]. - /// Throws a [ConfigFileNotFoundException] if the file is not specified Future getAndCheckIfConfigFileExists(String configFile) async { final file = File(configFile); if (!file.existsSync()) { @@ -32,11 +30,10 @@ class FileHelper { /// Ensures the specified [libsFolderName] directory exists and returns it. /// - /// If the directory does not exist, logs an error message via [LoggerHelper]. + /// Throws a [LibraryFolderNotFoundException] if the directory does not + /// exist. /// /// Returns the [Directory] representing the `lib/` folder. - /// Throws a [LibraryFolderNotFoundException] if the lib folder is not - /// available. Directory getAndCheckIfLibsDirectoryExists(String libsFolderName) { final projectDirectory = Directory(libsFolderName); diff --git a/lib/data/json_converter.dart b/lib/data/json_converter.dart index 49a2567..59f4791 100644 --- a/lib/data/json_converter.dart +++ b/lib/data/json_converter.dart @@ -1,6 +1,7 @@ import 'dart:convert'; -import 'package:eagle_eye/model/config_keys.dart'; +import 'package:eagle_eye/data/config_item_mapper.dart'; +import 'package:eagle_eye/data/config_keys.dart'; import 'package:eagle_eye/model/eagle_eye_config.dart'; import 'package:eagle_eye/model/eagle_eye_config_item.dart'; import 'package:eagle_eye/model/exceptions/invalid_config_exception.dart'; @@ -20,6 +21,13 @@ import 'package:eagle_eye/model/exceptions/invalid_json_key_exception.dart'; /// - `filePattern` — defines which files the rule applies to /// - `name` — optional human-readable rule name class JsonConverter { + /// Creates a new [JsonConverter]. + JsonConverter({required ConfigItemMapper mapper}) { + _mapper = mapper; + } + + late ConfigItemMapper _mapper; + void _checkKeys(List keys) { final validKeys = [ ConfigKeys.filePatternEagleItemKey, @@ -68,9 +76,7 @@ class JsonConverter { _checkKeys(jsonDataItem.keys.toList()); try { - EagleEyeConfigItem configItem = EagleEyeConfigItem.fromJson( - jsonDataItem, - ); + EagleEyeConfigItem configItem = _mapper.map(jsonDataItem); eagleConfigItems.add(configItem); } on InvalidConfigException catch (e) { throw InvalidConfigException('Rule #${index + 1}: ${e.details}'); diff --git a/lib/eagle_eye_launcher.dart b/lib/eagle_eye_launcher.dart index e5cd09d..404f07d 100644 --- a/lib/eagle_eye_launcher.dart +++ b/lib/eagle_eye_launcher.dart @@ -68,7 +68,7 @@ class EagleEyeLauncher { EagleEyeConfig configFile = await _locator.repository .getAndCheckIfConfigFileExists(); - List dartFiles = _locator.repository.allDartFiles(); + List dartPaths = _locator.repository.allDartPaths(); String applicationName = _locator.repository.getApplicationName(); @@ -77,14 +77,11 @@ class EagleEyeLauncher { '${EagleEyeRepositoryImpl.configFileName}', ); onCheck?.call('Application name: $applicationName'); - onCheck?.call('Found ${dartFiles.length} Dart file(s) in lib/'); + onCheck?.call('Found ${dartPaths.length} Dart file(s) in lib/'); _locator .createConfigValidator(onCheck: onCheck) - .validate( - config: configFile, - filePaths: dartFiles.map((file) => file.path), - ); + .validate(config: configFile, filePaths: dartPaths); final matcher = _locator.createMatcher( config: configFile, @@ -93,15 +90,15 @@ class EagleEyeLauncher { List errors = []; - for (final file in dartFiles) { - onCheck?.call('Checking ${file.path}'); + for (final path in dartPaths) { + onCheck?.call('Checking $path'); - final unit = _locator.parseDartFile(file.path); - EagleEyeConfigItem? eagleEyeItem = matcher.find(file.path); + final unit = _locator.dartSourceLoader.load(path); + EagleEyeConfigItem? eagleEyeItem = matcher.find(path); if (eagleEyeItem != null) { final visitor = _locator.createVisitor( configItem: eagleEyeItem, - filePath: file.path, + filePath: path, applicationName: applicationName, unit: unit, errorCallback: (error) => errors.add(error), @@ -114,7 +111,7 @@ class EagleEyeLauncher { } onCheck?.call( - 'Checked ${dartFiles.length} file(s); ' + 'Checked ${dartPaths.length} file(s); ' '${errors.length} violation(s) found', ); diff --git a/lib/model/eagle_eye_config_item.dart b/lib/model/eagle_eye_config_item.dart index 3726092..03dc006 100644 --- a/lib/model/eagle_eye_config_item.dart +++ b/lib/model/eagle_eye_config_item.dart @@ -1,6 +1,3 @@ -import 'package:eagle_eye/model/config_keys.dart'; -import 'package:eagle_eye/model/exceptions/invalid_config_exception.dart'; - /// Represents a single rule item in the Eagle Eye configuration file. /// /// Each [EagleEyeConfigItem] defines dependency constraints for files @@ -38,72 +35,4 @@ class EagleEyeConfigItem { required this.filePattern, this.name, }); - - /// Maps a single JSON object into an [EagleEyeConfigItem]. - /// - /// Throws an [InvalidConfigException] when a required field is missing or - /// when a field has an unexpected type. - factory EagleEyeConfigItem.fromJson(Map json) { - final dependenciesAllowed = _readDependenciesAllowed(json); - final forbiddenDependencies = _readStringList( - json, - ConfigKeys.forbiddenDependenciesEagleItemKey, - ); - final exclusiveDependencies = _readStringList( - json, - ConfigKeys.exclusiveDependenciesEagleItemKey, - ); - final filePattern = _readFilePattern(json); - final name = _readName(json); - - return EagleEyeConfigItem( - dependenciesAllowed: dependenciesAllowed, - forbiddenDependencies: forbiddenDependencies, - exclusiveDependencies: exclusiveDependencies, - filePattern: filePattern, - name: name, - ); - } - - static bool? _readDependenciesAllowed(Map json) { - final key = ConfigKeys.dependenciesAllowedEagleItemKey; - final value = json[key]; - if (value != null && value is! bool) { - throw InvalidConfigException('The "$key" value must be a boolean.'); - } - return value as bool?; - } - - static List? _readStringList(Map json, String key) { - final value = json[key]; - if (value == null) { - return null; - } - if (value is! List || value.any((item) => item is! String)) { - throw InvalidConfigException( - 'The "$key" value must be a list of strings.', - ); - } - return List.from(value); - } - - static String _readFilePattern(Map json) { - final key = ConfigKeys.filePatternEagleItemKey; - final value = json[key]; - if (value is! String || value.trim().isEmpty) { - throw InvalidConfigException( - 'The "$key" value is required and must be a non-empty string.', - ); - } - return value; - } - - static String? _readName(Map json) { - final key = ConfigKeys.nameEagleItemKey; - final value = json[key]; - if (value != null && value is! String) { - throw InvalidConfigException('The "$key" value must be a string.'); - } - return value as String?; - } } diff --git a/lib/service_locator.dart b/lib/service_locator.dart index ac906de..f7c8a5f 100644 --- a/lib/service_locator.dart +++ b/lib/service_locator.dart @@ -1,10 +1,10 @@ -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:eagle_eye/analyzer/config_validator.dart'; import 'package:eagle_eye/analyzer/eagle_eye_matcher.dart'; import 'package:eagle_eye/analyzer/eagle_eye_visitor.dart'; import 'package:eagle_eye/analyzer/regex_helper.dart'; +import 'package:eagle_eye/data/config_item_mapper.dart'; +import 'package:eagle_eye/data/dart_source_loader.dart'; import 'package:eagle_eye/data/eagle_eye_repository.dart'; import 'package:eagle_eye/data/file_helper.dart'; import 'package:eagle_eye/data/json_converter.dart'; @@ -25,9 +25,12 @@ class ServiceLocator { ServiceLocator(); final FileHelper _fileHelper = FileHelper(); - final JsonConverter _jsonConverter = JsonConverter(); + final JsonConverter _jsonConverter = JsonConverter( + mapper: ConfigItemMapper(), + ); final RegexHelper _regexHelper = RegexHelper(); final OutputRenderer _outputRenderer = OutputRenderer(); + final DartSourceLoader _dartSourceLoader = DartSourceLoader(); late final EagleEyeRepository _repository = EagleEyeRepositoryImpl( fileHelper: _fileHelper, @@ -43,17 +46,12 @@ class ServiceLocator { /// The renderer used to format detected violations. OutputRenderer get outputRenderer => _outputRenderer; + /// The loader used to read and parse Dart source files. + DartSourceLoader get dartSourceLoader => _dartSourceLoader; + /// Parses the command-line [args] into [CliOptions]. CliOptions parseCliOptions(List args) => CliOptions.parse(args); - /// Parses the Dart file at [path] into a [CompilationUnit]. - CompilationUnit parseDartFile(String path) { - return parseFile( - path: path, - featureSet: FeatureSet.latestLanguageVersion(), - ).unit; - } - /// Creates a configuration validator. EagleEyeConfigValidator createConfigValidator({CheckReporter? onCheck}) { return EagleEyeConfigValidator(regexHelper: _regexHelper, onCheck: onCheck); diff --git a/test/data/config_item_mapper_test.dart b/test/data/config_item_mapper_test.dart new file mode 100644 index 0000000..69ce2b1 --- /dev/null +++ b/test/data/config_item_mapper_test.dart @@ -0,0 +1,74 @@ +import 'package:eagle_eye/data/config_item_mapper.dart'; +import 'package:test/test.dart'; + +void main() { + group('ConfigItemMapper', () { + late ConfigItemMapper mapper; + + setUp(() { + mapper = ConfigItemMapper(); + }); + + test('dependenciesAllowed - returns a valid configuration item object', () { + final noDependsEnabled = { + 'filePattern': '*/data/model/*', + 'dependenciesAllowed': false, + }; + + final actual = mapper.map(noDependsEnabled); + expect(actual.dependenciesAllowed, false); + expect(actual.filePattern, '*/data/model/*'); + expect(null, actual.forbiddenDependencies); + expect(null, actual.exclusiveDependencies); + }); + + test( + 'forbiddenDependencies - returns a valid configuration item object', + () { + final doNotWith = { + 'filePattern': '*viewmodel.dart', + 'forbiddenDependencies': ['*_screen.dart'], + }; + + final actual = mapper.map(doNotWith); + + expect(actual.forbiddenDependencies, ['*_screen.dart']); + expect(actual.dependenciesAllowed, null); + expect(actual.filePattern, '*viewmodel.dart'); + expect(actual.exclusiveDependencies, null); + }, + ); + + test( + 'exclusiveDependencies - returns a valid configuration item object', + () { + final doNotWith = { + 'filePattern': '*screen.dart', + 'exclusiveDependencies': ['*_widget.dart'], + }; + + final actual = mapper.map(doNotWith); + + expect(actual.exclusiveDependencies, ['*_widget.dart']); + expect(actual.forbiddenDependencies, null); + expect(actual.dependenciesAllowed, null); + expect(actual.filePattern, '*screen.dart'); + }, + ); + + test('name - is optional and defaults to null when absent', () { + final actual = mapper.map({'filePattern': '*screen.dart'}); + + expect(actual.name, null); + }); + + test('name - maps a provided name', () { + final actual = mapper.map({ + 'filePattern': '*screen.dart', + 'name': 'screens-only-widgets', + }); + + expect(actual.name, 'screens-only-widgets'); + }); + }); +} diff --git a/test/data/dart_source_loader_test.dart b/test/data/dart_source_loader_test.dart new file mode 100644 index 0000000..bf48f4e --- /dev/null +++ b/test/data/dart_source_loader_test.dart @@ -0,0 +1,31 @@ +import 'dart:io'; + +import 'package:eagle_eye/data/dart_source_loader.dart'; +import 'package:test/test.dart'; + +void main() { + group('DartSourceLoader', () { + late DartSourceLoader loader; + late Directory directory; + + setUp(() { + loader = DartSourceLoader(); + directory = Directory.systemTemp.createTempSync('eagle_eye_loader'); + }); + + tearDown(() { + directory.deleteSync(recursive: true); + }); + + test('parses a Dart file into a compilation unit', () { + final file = File('${directory.path}/sample.dart'); + file.writeAsStringSync( + "import 'package:eagle_eye/service_locator.dart';", + ); + + final unit = loader.load(file.path); + + expect(unit.directives, hasLength(1)); + }); + }); +} diff --git a/test/data/file_helper_test.dart b/test/data/file_helper_test.dart new file mode 100644 index 0000000..2bcf2b6 --- /dev/null +++ b/test/data/file_helper_test.dart @@ -0,0 +1,45 @@ +import 'dart:io'; + +import 'package:eagle_eye/data/file_helper.dart'; +import 'package:eagle_eye/model/exceptions/empty_dart_file_list_exception.dart'; +import 'package:test/test.dart'; + +void main() { + group('FileHelper.allDartFiles', () { + late FileHelper fileHelper; + late Directory directory; + + setUp(() { + fileHelper = FileHelper(); + directory = Directory.systemTemp.createTempSync('eagle_eye_files'); + }); + + tearDown(() { + directory.deleteSync(recursive: true); + }); + + test('returns every Dart file recursively', () { + Directory('${directory.path}/nested').createSync(); + File('${directory.path}/root.dart').writeAsStringSync(''); + File('${directory.path}/nested/child.dart').writeAsStringSync(''); + File('${directory.path}/notes.txt').writeAsStringSync(''); + + final files = fileHelper.allDartFiles(directory); + + expect(files, hasLength(2)); + expect( + files.map((file) => file.uri.pathSegments.last), + containsAll(['root.dart', 'child.dart']), + ); + }); + + test('throws when no Dart files exist', () { + File('${directory.path}/notes.txt').writeAsStringSync(''); + + expect( + () => fileHelper.allDartFiles(directory), + throwsA(isA()), + ); + }); + }); +} diff --git a/test/data/json_converter_test.dart b/test/data/json_converter_test.dart index 6f18845..367abbb 100644 --- a/test/data/json_converter_test.dart +++ b/test/data/json_converter_test.dart @@ -1,3 +1,4 @@ +import 'package:eagle_eye/data/config_item_mapper.dart'; import 'package:eagle_eye/data/json_converter.dart'; import 'package:eagle_eye/model/exceptions/invalid_config_exception.dart'; import 'package:eagle_eye/model/exceptions/invalid_json_key_exception.dart'; @@ -8,7 +9,7 @@ void main() { late JsonConverter converter; setUp(() { - converter = JsonConverter(); + converter = JsonConverter(mapper: ConfigItemMapper()); }); Matcher throwsInvalidConfig(String expectedMessage) { diff --git a/test/model/eagle_eye_config_item_test.dart b/test/model/eagle_eye_config_item_test.dart deleted file mode 100644 index 06fee58..0000000 --- a/test/model/eagle_eye_config_item_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:eagle_eye/model/eagle_eye_config_item.dart'; -import 'package:test/test.dart'; - -void main() { - test('dependenciesAllowed - returns a valid configuration item object', () { - final noDependsEnabled = { - 'filePattern': '*/data/model/*', - 'dependenciesAllowed': false - }; - - final actual = EagleEyeConfigItem.fromJson(noDependsEnabled); - expect( - actual.dependenciesAllowed, - false, - ); - expect(actual.filePattern, '*/data/model/*'); - expect(null, actual.forbiddenDependencies); - expect(null, actual.exclusiveDependencies); - }); - - test('forbiddenDependencies - returns a valid configuration item object', () { - final doNotWith = { - 'filePattern': '*viewmodel.dart', - 'forbiddenDependencies': ['*_screen.dart'] - }; - - final actual = EagleEyeConfigItem.fromJson(doNotWith); - - expect( - actual.forbiddenDependencies, - ['*_screen.dart'], - ); - expect(actual.dependenciesAllowed, null); - expect(actual.filePattern, '*viewmodel.dart'); - expect(actual.exclusiveDependencies, null); - }); - - test('exclusiveDependencies - returns a valid configuration item object', () { - final doNotWith = { - 'filePattern': '*screen.dart', - 'exclusiveDependencies': ['*_widget.dart'] - }; - - final actual = EagleEyeConfigItem.fromJson(doNotWith); - - expect(actual.exclusiveDependencies, ['*_widget.dart']); - expect(actual.forbiddenDependencies, null); - expect(actual.dependenciesAllowed, null); - expect(actual.filePattern, '*screen.dart'); - }); -} diff --git a/test/service_locator_test.dart b/test/service_locator_test.dart index 8e6adbb..29a0a08 100644 --- a/test/service_locator_test.dart +++ b/test/service_locator_test.dart @@ -38,7 +38,7 @@ void main() { expect(validator.regexHelper, same(locator.regexHelper)); }); - test('parses a Dart file into a compilation unit', () { + test('exposes a Dart source loader', () { final directory = Directory.systemTemp.createTempSync('eagle_eye'); addTearDown(() => directory.deleteSync(recursive: true)); final file = File('${directory.path}/sample.dart'); @@ -46,7 +46,7 @@ void main() { "import 'package:eagle_eye/service_locator.dart';", ); - final unit = locator.parseDartFile(file.path); + final unit = locator.dartSourceLoader.load(file.path); expect(unit.directives, hasLength(1)); });