Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion tool/dart_hooks/lib/src/dart_analyze_hook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ class DartAnalyzeHook extends BaseHook {
@override
String get hookName => 'dart analyze';

/// The key in `dart_hooks.yaml` that enables this hook.
static const String configKeyName = 'DartAnalyzeHook';

@override
String get configKey => 'DartAnalyzeHook';
String get configKey => configKeyName;

@override
Future<ProcessResult> executeCommand(List<String> files) {
Expand Down
5 changes: 4 additions & 1 deletion tool/dart_hooks/lib/src/dart_format_hook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ class DartFormatHook extends BaseHook {
@override
String get hookName => 'dart format';

/// The key in `dart_hooks.yaml` that enables this hook.
static const String configKeyName = 'DartFormatHook';

@override
String get configKey => 'DartFormatHook';
String get configKey => configKeyName;

@override
Future<ProcessResult> executeCommand(List<String> files) {
Expand Down
70 changes: 70 additions & 0 deletions tool/dart_hooks/test/example_config_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';
import 'package:dart_hooks/src/dart_analyze_hook.dart';
import 'package:dart_hooks/src/dart_format_hook.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';

/// Verifies that the `dart_hooks.yaml` files committed to the repository
/// actually enable the hooks. The committed files are the examples users copy,
/// and a key that does not match a hook's `configKey` leaves that hook silently
/// disabled (see https://github.com/flutter/skills/issues/150).
///
/// This mirrors the enablement gate in `BaseHook.run()`
/// (`yaml.containsKey(configKey)` && `yaml[configKey] == true`) and reads the
/// keys from the same `configKeyName` constants the hooks use, so the shipped
/// files, the code, and this test cannot drift apart.
void main() {
group('Shipped dart_hooks.yaml examples', () {
// `dart test` runs with the package root as the current directory. The
// committed example files live there and at the surrounding monorepo
// locations.
final String packageRoot = Directory.current.path;
final candidatePaths = <String>[
path.join(packageRoot, 'dart_hooks.yaml'),
path.join(packageRoot, '..', 'dart_hooks.yaml'),
path.join(packageRoot, '..', '..', 'dart_hooks.yaml'),
path.join(packageRoot, '..', 'dart_skills_lint', 'dart_hooks.yaml'),
];
Comment on lines +26 to +32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When running tests from the monorepo root (e.g., dart test tool/dart_hooks), Directory.current points to the monorepo root rather than the package root. This causes the relative paths in candidatePaths to resolve incorrectly, resulting in 3 out of the 4 configuration files being silently skipped during verification.

Adjusting packageRoot to point to the actual package directory when run from the monorepo root ensures all 4 configuration files are always verified.

    var packageRoot = Directory.current.path;
    final toolDartHooks = path.join(packageRoot, 'tool', 'dart_hooks');
    if (Directory(toolDartHooks).existsSync()) {
      packageRoot = toolDartHooks;
    }
    final candidatePaths = <String>[
      path.join(packageRoot, 'dart_hooks.yaml'),
      path.join(packageRoot, '..', 'dart_hooks.yaml'),
      path.join(packageRoot, '..', '..', 'dart_hooks.yaml'),
      path.join(packageRoot, '..', 'dart_skills_lint', 'dart_hooks.yaml'),
    ];


final List<String> existingPaths = candidatePaths.where((p) => File(p).existsSync()).toList();

test('at least one shipped example was found to verify', () {
// Guards against a wrong working directory silently passing the suite.
expect(
existingPaths,
isNotEmpty,
reason:
'No committed dart_hooks.yaml files were found relative to '
'$packageRoot. Checked: $candidatePaths',
);
});

for (final filePath in existingPaths) {
test('$filePath enables both hooks', () {
final dynamic yaml = loadYaml(File(filePath).readAsStringSync());
expect(yaml, isA<Map<dynamic, dynamic>>(), reason: '$filePath is not a YAML map.');
final yamlMap = yaml as Map<dynamic, dynamic>;

expect(
yamlMap[DartFormatHook.configKeyName],
isTrue,
reason:
'$filePath must enable the format hook with '
'"${DartFormatHook.configKeyName}: true".',
);
expect(
yamlMap[DartAnalyzeHook.configKeyName],
isTrue,
reason:
'$filePath must enable the analyze hook with '
'"${DartAnalyzeHook.configKeyName}: true".',
);
});
}
});
}
6 changes: 4 additions & 2 deletions tool/dart_hooks/test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';
import 'package:dart_hooks/src/dart_analyze_hook.dart';
import 'package:dart_hooks/src/dart_format_hook.dart';
import 'package:dart_hooks/src/process_runner.dart';

/// A mock implementation of [ProcessRunner] that delegates to a function.
Expand Down Expand Up @@ -31,7 +33,7 @@ class MockProcessRunner implements ProcessRunner {
}

/// Returns a YAML configuration string enabling or disabling the analyze hook.
String mockAnalyzeConfig(bool enabled) => 'DartAnalyzeHook: $enabled\n';
String mockAnalyzeConfig(bool enabled) => '${DartAnalyzeHook.configKeyName}: $enabled\n';

/// Returns a YAML configuration string enabling or disabling the format hook.
String mockFormatConfig(bool enabled) => 'DartFormatHook: $enabled\n';
String mockFormatConfig(bool enabled) => '${DartFormatHook.configKeyName}: $enabled\n';
Loading