blob: 7ccfb123eaf526b2352416dafd157aa388b72c34 [file]
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:test/test.dart';
import '../util.dart';
void main() {
late Directory packagesDir;
setUp(() {
(:packagesDir, processRunner: _, gitProcessRunner: _, gitDir: _) = configureBaseCommandMocks();
});
group('displayName', () {
test('prints packageDir-relative paths by default', () async {
expect(RepositoryPackage(packagesDir.childDirectory('foo')).displayName, 'foo');
expect(
RepositoryPackage(
packagesDir.childDirectory('foo').childDirectory('bar').childDirectory('baz'),
).displayName,
'foo/bar/baz',
);
});
test('handles third_party/packages/', () async {
expect(
RepositoryPackage(
packagesDir.parent
.childDirectory('third_party')
.childDirectory('packages')
.childDirectory('foo')
.childDirectory('bar')
.childDirectory('baz'),
).displayName,
'foo/bar/baz',
);
});
test('always uses Posix-style paths', () async {
final Directory windowsPackagesDir = createPackagesDirectory(
MemoryFileSystem(style: FileSystemStyle.windows),
);
expect(RepositoryPackage(windowsPackagesDir.childDirectory('foo')).displayName, 'foo');
expect(
RepositoryPackage(
windowsPackagesDir.childDirectory('foo').childDirectory('bar').childDirectory('baz'),
).displayName,
'foo/bar/baz',
);
});
test('elides group name in grouped federated plugin structure', () async {
expect(
RepositoryPackage(
packagesDir.childDirectory('a_plugin').childDirectory('a_plugin_platform_interface'),
).displayName,
'a_plugin_platform_interface',
);
expect(
RepositoryPackage(
packagesDir.childDirectory('a_plugin').childDirectory('a_plugin_platform_web'),
).displayName,
'a_plugin_platform_web',
);
});
// The app-facing package doesn't get elided to avoid potential confusion
// with the group folder itself.
test('does not elide group name for app-facing packages', () async {
expect(
RepositoryPackage(
packagesDir.childDirectory('a_plugin').childDirectory('a_plugin'),
).displayName,
'a_plugin/a_plugin',
);
});
});
group('getExamples', () {
test('handles a single Flutter example', () async {
final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir);
final List<RepositoryPackage> examples = plugin.getExamples().toList();
expect(examples.length, 1);
expect(examples[0].isExample, isTrue);
expect(examples[0].path, getExampleDir(plugin).path);
});
test('handles multiple Flutter examples', () async {
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir,
examples: <String>['example1', 'example2'],
);
final List<RepositoryPackage> examples = plugin.getExamples().toList();
expect(examples.length, 2);
expect(examples[0].isExample, isTrue);
expect(examples[1].isExample, isTrue);
expect(examples[0].path, getExampleDir(plugin).childDirectory('example1').path);
expect(examples[1].path, getExampleDir(plugin).childDirectory('example2').path);
});
test('handles a single non-Flutter example', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
final List<RepositoryPackage> examples = package.getExamples().toList();
expect(examples.length, 1);
expect(examples[0].isExample, isTrue);
expect(examples[0].path, getExampleDir(package).path);
});
test('handles multiple non-Flutter examples', () async {
final RepositoryPackage package = createFakePackage(
'a_package',
packagesDir,
examples: <String>['example1', 'example2'],
);
final List<RepositoryPackage> examples = package.getExamples().toList();
expect(examples.length, 2);
expect(examples[0].isExample, isTrue);
expect(examples[1].isExample, isTrue);
expect(examples[0].path, getExampleDir(package).childDirectory('example1').path);
expect(examples[1].path, getExampleDir(package).childDirectory('example2').path);
});
});
group('federated plugin queries', () {
test('all return false for a simple plugin', () {
final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir);
expect(plugin.isFederated, false);
expect(plugin.isAppFacing, false);
expect(plugin.isPlatformInterface, false);
expect(plugin.isFederated, false);
expect(plugin.isExample, isFalse);
});
test('handle app-facing packages', () {
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir.childDirectory('a_plugin'),
);
expect(plugin.isFederated, true);
expect(plugin.isAppFacing, true);
expect(plugin.isPlatformInterface, false);
expect(plugin.isPlatformImplementation, false);
expect(plugin.isExample, isFalse);
});
test('handle platform interface packages', () {
final RepositoryPackage plugin = createFakePlugin(
'a_plugin_platform_interface',
packagesDir.childDirectory('a_plugin'),
);
expect(plugin.isFederated, true);
expect(plugin.isAppFacing, false);
expect(plugin.isPlatformInterface, true);
expect(plugin.isPlatformImplementation, false);
expect(plugin.isExample, isFalse);
});
test('handle platform implementation packages', () {
// A platform interface can end with anything, not just one of the known
// platform names, because of cases like webview_flutter_wkwebview.
final RepositoryPackage plugin = createFakePlugin(
'a_plugin_foo',
packagesDir.childDirectory('a_plugin'),
);
expect(plugin.isFederated, true);
expect(plugin.isAppFacing, false);
expect(plugin.isPlatformInterface, false);
expect(plugin.isPlatformImplementation, true);
expect(plugin.isExample, isFalse);
});
});
group('isPubIgnored', () {
test('returns false if there is no enclosing package', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
expect(package.isPubIgnored, false);
});
test('returns false if there is no .pubignore file', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
final RepositoryPackage subPackage = createFakePackage('sub_package', package.directory);
expect(subPackage.isPubIgnored, false);
});
test('returns true if the package is in an ignored directory (with trailing slash)', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('.agents/');
final Directory agentsDir = package.directory.childDirectory('.agents')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', agentsDir);
expect(subPackage.isPubIgnored, true);
});
test(
'returns true if the package is in an ignored directory (without trailing slash)',
() async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('.agents');
final Directory agentsDir = package.directory.childDirectory('.agents')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', agentsDir);
expect(subPackage.isPubIgnored, true);
},
);
test('returns true if a deeply nested package is in an ignored directory', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('.agents/');
final Directory nestedDir =
package.directory.childDirectory('.agents').childDirectory('skills')
..createSync(recursive: true);
final RepositoryPackage subPackage = createFakePackage('sub_package', nestedDir);
expect(subPackage.isPubIgnored, true);
});
test('returns false if the package is not in an ignored directory', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('.agents/');
final Directory otherDir = package.directory.childDirectory('other')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', otherDir);
expect(subPackage.isPubIgnored, false);
});
test('ignores comments and empty lines in .pubignore', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('\n# a comment\n.agents/\n');
final Directory agentsDir = package.directory.childDirectory('.agents')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', agentsDir);
expect(subPackage.isPubIgnored, true);
});
test('respects anchored patterns', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('/.agents/');
final Directory agentsDir = package.directory.childDirectory('.agents')..createSync();
final RepositoryPackage subPackage1 = createFakePackage('sub_package1', agentsDir);
final Directory nestedAgentsDir =
package.directory.childDirectory('other').childDirectory('.agents')
..createSync(recursive: true);
final RepositoryPackage subPackage2 = createFakePackage('sub_package2', nestedAgentsDir);
expect(subPackage1.isPubIgnored, true);
expect(subPackage2.isPubIgnored, false);
});
test('handles trailing slashes correctly', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('foo/');
final Directory fooDir = package.directory.childDirectory('foo')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', fooDir);
expect(subPackage.isPubIgnored, true);
});
test('gracefully ignores malformed glob patterns', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.directory.childFile('.pubignore').writeAsStringSync('[unclosed bracket\n.agents/');
final Directory agentsDir = package.directory.childDirectory('.agents')..createSync();
final RepositoryPackage subPackage = createFakePackage('sub_package', agentsDir);
// Should not throw, and should still match valid patterns in the file
expect(subPackage.isPubIgnored, true);
});
});
group('pubspec', () {
test('file', () async {
final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir);
final File pubspecFile = plugin.pubspecFile;
expect(pubspecFile.path, plugin.directory.childFile('pubspec.yaml').path);
});
test('parsing', () async {
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir,
examples: <String>['example1', 'example2'],
);
final Pubspec pubspec = plugin.parsePubspec();
expect(pubspec.name, 'a_plugin');
});
});
group('requiresFlutter', () {
test('returns true for Flutter package', () async {
final RepositoryPackage package = createFakePackage(
'a_package',
packagesDir,
isFlutter: true,
);
expect(package.requiresFlutter(), true);
});
test('returns true for a dev dependency on Flutter', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
final File pubspecFile = package.pubspecFile;
final Pubspec pubspec = package.parsePubspec();
pubspec.devDependencies['flutter'] = SdkDependency('flutter');
pubspecFile.writeAsStringSync(pubspec.toString());
expect(package.requiresFlutter(), true);
});
test('returns true for a dev dependency on other Flutter SDK packages', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
final File pubspecFile = package.pubspecFile;
final Pubspec pubspec = package.parsePubspec();
pubspec.devDependencies['flutter_test'] = SdkDependency('flutter');
pubspecFile.writeAsStringSync(pubspec.toString());
expect(package.requiresFlutter(), true);
});
test('returns false for non-Flutter package', () async {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
expect(package.requiresFlutter(), false);
});
});
group('getPendingChangelogs', () {
test('returns an error if the directory is missing', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
expect(() => package.getPendingChangelogs(), throwsFormatException);
});
test('returns empty lists if the directory is empty', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.pendingChangelogsDirectory.createSync();
final List<PendingChangelogEntry> changelogs = package.getPendingChangelogs();
expect(changelogs, isEmpty);
});
test('returns entries for valid files', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.pendingChangelogsDirectory.createSync();
package.pendingChangelogsDirectory.childFile('a.yaml').writeAsStringSync('''
changelog: A
version: patch
''');
package.pendingChangelogsDirectory.childFile('b.yaml').writeAsStringSync('''
changelog: B
version: minor
''');
final List<PendingChangelogEntry> changelogs = package.getPendingChangelogs();
expect(changelogs, hasLength(2));
expect(changelogs[0].changelog, 'A');
expect(changelogs[0].version, VersionChange.patch);
expect(changelogs[1].changelog, 'B');
expect(changelogs[1].version, VersionChange.minor);
});
test('returns an error for a malformed file', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.pendingChangelogsDirectory.createSync();
final File changelogFile = package.pendingChangelogsDirectory.childFile('a.yaml');
changelogFile.writeAsStringSync('not yaml');
expect(
() => package.getPendingChangelogs(),
throwsA(
isA<FormatException>().having(
(FormatException e) => e.message,
'message',
contains('Expected a YAML map, but found String'),
),
),
);
});
test('ignores template.yaml', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.pendingChangelogsDirectory.createSync();
package.pendingChangelogsDirectory.childFile('a.yaml').writeAsStringSync('''
changelog: A
version: patch
''');
package.pendingChangelogsDirectory.childFile('template.yaml').writeAsStringSync('''
changelog: TEMPLATE
version: skip
''');
final List<PendingChangelogEntry> changelogs = package.getPendingChangelogs();
expect(changelogs, hasLength(1));
expect(changelogs[0].changelog, 'A');
});
test('returns an error for non-YAML files', () {
final RepositoryPackage package = createFakePackage('a_package', packagesDir);
package.pendingChangelogsDirectory.createSync();
package.pendingChangelogsDirectory
.childFile('readme.txt')
.writeAsStringSync('This is not a YAML file.');
expect(
() => package.getPendingChangelogs(),
throwsA(
isA<FormatException>().having(
(FormatException e) => e.message,
'message',
contains('Found non-YAML file(s) in pending_changelogs'),
),
),
);
});
});
}