Unverified Commit 9bc533c9 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] do not error flutter doctor on missing AS/intellij plugins (#66782)

In cases where the Intellij/AS plugins are not located, display links to where they can be downloaded but do not surface an error. This should generally reduce confusion about whether the plugins are required for every installed IDE. For example, frequently users may only install AS so that they can install the Android SDK - or they may have multiple copies of Intellij installed.

For example: #66762
parent e8812c40
......@@ -2,26 +2,33 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/config.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/user_messages.dart';
import '../base/version.dart';
import '../doctor.dart';
import '../globals.dart' as globals;
import '../intellij/intellij.dart';
import 'android_studio.dart';
class AndroidStudioValidator extends DoctorValidator {
AndroidStudioValidator(this._studio) : super('Android Studio');
AndroidStudioValidator(this._studio, { @required FileSystem fileSystem })
: _fileSystem = fileSystem,
super('Android Studio');
final AndroidStudio _studio;
final FileSystem _fileSystem;
static List<DoctorValidator> get allValidators {
static List<DoctorValidator> allValidators(Config config, Platform platform, FileSystem fileSystem, UserMessages userMessages) {
final List<AndroidStudio> studios = AndroidStudio.allInstalled();
return <DoctorValidator>[
if (studios.isEmpty)
NoAndroidStudioValidator()
NoAndroidStudioValidator(config: config, platform: platform, userMessages: userMessages)
else
...studios.map<DoctorValidator>(
(AndroidStudio studio) => AndroidStudioValidator(studio)
(AndroidStudio studio) => AndroidStudioValidator(studio, fileSystem: fileSystem)
),
];
}
......@@ -38,14 +45,20 @@ class AndroidStudioValidator extends DoctorValidator {
userMessages.androidStudioLocation(_studio.directory),
));
final IntelliJPlugins plugins = IntelliJPlugins(_studio.pluginsPath);
final IntelliJPlugins plugins = IntelliJPlugins(_studio.pluginsPath, fileSystem: _fileSystem);
plugins.validatePackage(
messages,
<String>['flutter-intellij', 'flutter-intellij.jar'],
'Flutter',
IntelliJPlugins.kIntellijFlutterPluginUrl,
minVersion: IntelliJPlugins.kMinFlutterPluginVersion,
);
plugins.validatePackage(messages, <String>['Dart'], 'Dart');
plugins.validatePackage(
messages,
<String>['Dart'],
'Dart',
IntelliJPlugins.kIntellijDartPluginUrl,
);
if (_studio.isValid) {
type = _hasIssues(messages)
......@@ -74,21 +87,32 @@ class AndroidStudioValidator extends DoctorValidator {
}
class NoAndroidStudioValidator extends DoctorValidator {
NoAndroidStudioValidator() : super('Android Studio');
NoAndroidStudioValidator({
@required Config config,
@required Platform platform,
@required UserMessages userMessages,
}) : _config = config,
_platform = platform,
_userMessages = userMessages,
super('Android Studio');
final Config _config;
final Platform _platform;
final UserMessages _userMessages;
@override
Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[];
final String cfgAndroidStudio = globals.config.getValue(
final String cfgAndroidStudio = _config.getValue(
'android-studio-dir',
) as String;
if (cfgAndroidStudio != null) {
messages.add(ValidationMessage.error(
userMessages.androidStudioMissing(cfgAndroidStudio),
_userMessages.androidStudioMissing(cfgAndroidStudio),
));
}
messages.add(ValidationMessage(userMessages.androidStudioInstallation(globals.platform)));
messages.add(ValidationMessage(_userMessages.androidStudioInstallation(_platform)));
return ValidationResult(
ValidationType.notAvailable,
......
......@@ -11,6 +11,7 @@ import 'base/async_guard.dart';
import 'base/context.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'base/platform.dart';
import 'base/process.dart';
import 'base/terminal.dart';
import 'base/user_messages.dart';
......@@ -75,8 +76,8 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
}
final List<DoctorValidator> ideValidators = <DoctorValidator>[
...AndroidStudioValidator.allValidators,
...IntelliJValidator.installedValidators,
...AndroidStudioValidator.allValidators(globals.config, globals.platform, globals.fs, globals.userMessages),
...IntelliJValidator.installedValidators(globals.fs, globals.platform),
...VsCodeValidator.installedValidators,
];
final ProxyValidator proxyValidator = ProxyValidator(platform: globals.platform);
......@@ -342,6 +343,9 @@ class Doctor {
hangingIndent = 0;
indent = 6;
}
if (message.contextUrl != null) {
_logger.printStatus('🔨 ${message.contextUrl}', hangingIndent: hangingIndent, indent: indent, emphasis: true);
}
}
}
if (verbose) {
......@@ -573,16 +577,40 @@ class ValidationResult {
}
}
/// A status line for the flutter doctor validation to display.
///
/// The [message] is required and represents either an informational statement
/// about the particular doctor validation that passed, or more context
/// on the cause and/or solution to the validation failure.
@immutable
class ValidationMessage {
const ValidationMessage(this.message) : type = ValidationMessageType.information;
const ValidationMessage.error(this.message) : type = ValidationMessageType.error;
const ValidationMessage.hint(this.message) : type = ValidationMessageType.hint;
/// Create a validation message with information for a passing validatior.
///
/// By default this is not displayed unless the doctor is run in
/// verbose mode.
///
/// The [contextUrl] may be supplied to link to external resources. This
/// is displayed after the informative message in verbose modes.
const ValidationMessage(this.message, {this.contextUrl}) : type = ValidationMessageType.information;
/// Create a validation message with information for a failing validator.
const ValidationMessage.error(this.message)
: type = ValidationMessageType.error,
contextUrl = null;
/// Create a validation message with information for a partially failing
/// validator.
const ValidationMessage.hint(this.message)
: type = ValidationMessageType.hint,
contextUrl = null;
final ValidationMessageType type;
final String contextUrl;
final String message;
bool get isError => type == ValidationMessageType.error;
bool get isHint => type == ValidationMessageType.hint;
final String message;
String get indicator {
switch (type) {
......@@ -613,16 +641,14 @@ class ValidationMessage {
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is ValidationMessage
&& other.message == message
&& other.type == type;
&& other.type == type
&& other.contextUrl == contextUrl;
}
@override
int get hashCode => type.hashCode ^ message.hashCode;
int get hashCode => type.hashCode ^ message.hashCode ^ contextUrl.hashCode;
}
class FlutterValidator extends DoctorValidator {
......@@ -709,10 +735,15 @@ class NoIdeValidator extends DoctorValidator {
}
}
/// A doctor validator for both Intellij and Android Studio.
abstract class IntelliJValidator extends DoctorValidator {
IntelliJValidator(String title, this.installPath) : super(title);
IntelliJValidator(String title, this.installPath, {
@required FileSystem fileSystem,
}) : _fileSystem = fileSystem,
super(title);
final String installPath;
final FileSystem _fileSystem;
String get version;
String get pluginsPath;
......@@ -724,12 +755,12 @@ abstract class IntelliJValidator extends DoctorValidator {
static final Version kMinIdeaVersion = Version(2017, 1, 0);
static Iterable<DoctorValidator> get installedValidators {
if (globals.platform.isLinux || globals.platform.isWindows) {
return IntelliJValidatorOnLinuxAndWindows.installed;
static Iterable<DoctorValidator> installedValidators(FileSystem fileSystem, Platform platform) {
if (platform.isLinux || platform.isWindows) {
return IntelliJValidatorOnLinuxAndWindows.installed(fileSystem);
}
if (globals.platform.isMacOS) {
return IntelliJValidatorOnMac.installed;
if (platform.isMacOS) {
return IntelliJValidatorOnMac.installed(fileSystem);
}
return <DoctorValidator>[];
}
......@@ -743,10 +774,20 @@ abstract class IntelliJValidator extends DoctorValidator {
} else {
messages.add(ValidationMessage(userMessages.intellijLocation(installPath)));
final IntelliJPlugins plugins = IntelliJPlugins(pluginsPath);
plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
'Flutter', minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
plugins.validatePackage(messages, <String>['Dart'], 'Dart');
final IntelliJPlugins plugins = IntelliJPlugins(pluginsPath, fileSystem: _fileSystem);
plugins.validatePackage(
messages,
<String>['flutter-intellij', 'flutter-intellij.jar'],
'Flutter',
IntelliJPlugins.kIntellijFlutterPluginUrl,
minVersion: IntelliJPlugins.kMinFlutterPluginVersion,
);
plugins.validatePackage(
messages,
<String>['Dart'],
'Dart',
IntelliJPlugins.kIntellijDartPluginUrl,
);
if (_hasIssues(messages)) {
messages.add(ValidationMessage(userMessages.intellijPluginInfo));
......@@ -758,7 +799,8 @@ abstract class IntelliJValidator extends DoctorValidator {
return ValidationResult(
_hasIssues(messages) ? ValidationType.partial : ValidationType.installed,
messages,
statusInfo: userMessages.intellijStatusInfo(version));
statusInfo: userMessages.intellijStatusInfo(version),
);
}
bool _hasIssues(List<ValidationMessage> messages) {
......@@ -782,8 +824,11 @@ abstract class IntelliJValidator extends DoctorValidator {
}
}
/// A linux and windows specific implementation of the intellij validator.
class IntelliJValidatorOnLinuxAndWindows extends IntelliJValidator {
IntelliJValidatorOnLinuxAndWindows(String title, this.version, String installPath, this.pluginsPath) : super(title, installPath);
IntelliJValidatorOnLinuxAndWindows(String title, this.version, String installPath, this.pluginsPath, {
@required FileSystem fileSystem,
}) : super(title, installPath, fileSystem: fileSystem);
@override
final String version;
......@@ -791,7 +836,7 @@ class IntelliJValidatorOnLinuxAndWindows extends IntelliJValidator {
@override
final String pluginsPath;
static Iterable<DoctorValidator> get installed {
static Iterable<DoctorValidator> installed(FileSystem fileSystem) {
final List<DoctorValidator> validators = <DoctorValidator>[];
if (globals.fsUtils.homeDirPath == null) {
return validators;
......@@ -799,7 +844,7 @@ class IntelliJValidatorOnLinuxAndWindows extends IntelliJValidator {
void addValidator(String title, String version, String installPath, String pluginsPath) {
final IntelliJValidatorOnLinuxAndWindows validator =
IntelliJValidatorOnLinuxAndWindows(title, version, installPath, pluginsPath);
IntelliJValidatorOnLinuxAndWindows(title, version, installPath, pluginsPath, fileSystem: fileSystem);
for (int index = 0; index < validators.length; ++index) {
final DoctorValidator other = validators[index];
if (other is IntelliJValidatorOnLinuxAndWindows && validator.installPath == other.installPath) {
......@@ -835,8 +880,11 @@ class IntelliJValidatorOnLinuxAndWindows extends IntelliJValidator {
}
}
/// A macOS specific implementation of the intellij validator.
class IntelliJValidatorOnMac extends IntelliJValidator {
IntelliJValidatorOnMac(String title, this.id, String installPath) : super(title, installPath);
IntelliJValidatorOnMac(String title, this.id, String installPath, {
@required FileSystem fileSystem,
}) : super(title, installPath, fileSystem: fileSystem);
final String id;
......@@ -846,26 +894,26 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
'IntelliJ IDEA CE.app': 'IdeaIC',
};
static Iterable<DoctorValidator> get installed {
static Iterable<DoctorValidator> installed(FileSystem fileSystem) {
final List<DoctorValidator> validators = <DoctorValidator>[];
final List<String> installPaths = <String>[
'/Applications',
globals.fs.path.join(globals.fsUtils.homeDirPath, 'Applications'),
fileSystem.path.join(globals.fsUtils.homeDirPath, 'Applications'),
];
void checkForIntelliJ(Directory dir) {
final String name = globals.fs.path.basename(dir.path);
final String name = fileSystem.path.basename(dir.path);
_dirNameToId.forEach((String dirName, String id) {
if (name == dirName) {
final String title = IntelliJValidator._idToTitle[id];
validators.add(IntelliJValidatorOnMac(title, id, dir.path));
validators.add(IntelliJValidatorOnMac(title, id, dir.path, fileSystem: fileSystem));
}
});
}
try {
final Iterable<Directory> installDirs = installPaths
.map<Directory>((String installPath) => globals.fs.directory(installPath))
.map(fileSystem.directory)
.map<List<FileSystemEntity>>((Directory dir) => dir.existsSync() ? dir.listSync() : <FileSystemEntity>[])
.expand<FileSystemEntity>((List<FileSystemEntity> mappedDirs) => mappedDirs)
.whereType<Directory>();
......@@ -892,7 +940,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
@visibleForTesting
String get plistFile {
_plistFile ??= globals.fs.path.join(installPath, 'Contents', 'Info.plist');
_plistFile ??= _fileSystem.path.join(installPath, 'Contents', 'Info.plist');
return _plistFile;
}
String _plistFile;
......
......@@ -3,23 +3,30 @@
// found in the LICENSE file.
import 'package:archive/archive.dart';
import 'package:meta/meta.dart';
import '../base/file_system.dart';
import '../base/version.dart';
import '../convert.dart';
import '../doctor.dart';
import '../globals.dart' as globals;
class IntelliJPlugins {
IntelliJPlugins(this.pluginsPath);
IntelliJPlugins(this.pluginsPath, {
@required FileSystem fileSystem
}) : _fileSystem = fileSystem;
final FileSystem _fileSystem;
final String pluginsPath;
static final Version kMinFlutterPluginVersion = Version(16, 0, 0);
static const String kIntellijDartPluginUrl = 'https://plugins.jetbrains.com/plugin/6351-dart';
static const String kIntellijFlutterPluginUrl = 'https://plugins.jetbrains.com/plugin/9212-flutter';
void validatePackage(
List<ValidationMessage> messages,
List<String> packageNames,
String title, {
String title,
String url, {
Version minVersion,
}) {
for (final String packageName in packageNames) {
......@@ -31,36 +38,36 @@ class IntelliJPlugins {
final Version version = Version.parse(versionText);
if (version != null && minVersion != null && version < minVersion) {
messages.add(ValidationMessage.error(
'$title plugin version $versionText - the recommended minimum version is $minVersion'));
'$title plugin version $versionText - the recommended minimum version is $minVersion'),
);
} else {
messages.add(ValidationMessage(
'$title plugin ${version != null ? "version $version" : "installed"}'));
'$title plugin ${version != null ? "version $version" : "installed"}'),
);
}
return;
}
messages.add(ValidationMessage.error(
'$title plugin not installed; this adds $title specific functionality.'));
messages.add(ValidationMessage(
'$title plugin can be installed from:',
contextUrl: url,
));
}
bool _hasPackage(String packageName) {
final String packagePath = globals.fs.path.join(pluginsPath, packageName);
final String packagePath = _fileSystem.path.join(pluginsPath, packageName);
if (packageName.endsWith('.jar')) {
return globals.fs.isFileSync(packagePath);
return _fileSystem.isFileSync(packagePath);
}
return globals.fs.isDirectorySync(packagePath);
return _fileSystem.isDirectorySync(packagePath);
}
String _readPackageVersion(String packageName) {
final String jarPath = packageName.endsWith('.jar')
? globals.fs.path.join(pluginsPath, packageName)
: globals.fs.path.join(pluginsPath, packageName, 'lib', '$packageName.jar');
// TODO(danrubel): look for a better way to extract a single 2K file from the zip
// rather than reading the entire file into memory.
? _fileSystem.path.join(pluginsPath, packageName)
: _fileSystem.path.join(pluginsPath, packageName, 'lib', '$packageName.jar');
try {
final Archive archive =
ZipDecoder().decodeBytes(globals.fs.file(jarPath).readAsBytesSync());
ZipDecoder().decodeBytes(_fileSystem.file(jarPath).readAsBytesSync());
final ArchiveFile file = archive.findFile('META-INF/plugin.xml');
final String content = utf8.decode(file.content as List<int>);
const String versionStartTag = '<version>';
......
......@@ -27,6 +27,7 @@ import 'package:flutter_tools/src/version.dart';
import 'package:flutter_tools/src/vscode/vscode.dart';
import 'package:flutter_tools/src/vscode/vscode_validator.dart';
import 'package:flutter_tools/src/web/workflow.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:fake_async/fake_async.dart';
......@@ -61,6 +62,16 @@ void main() {
logger = BufferLogger.test();
});
testWithoutContext('ValidationMessage equality and hashCode includes contextUrl', () {
const ValidationMessage messageA = ValidationMessage('ab', contextUrl: 'a');
const ValidationMessage messageB = ValidationMessage('ab', contextUrl: 'b');
expect(messageB, isNot(messageA));
expect(messageB.hashCode, isNot(messageA.hashCode));
expect(messageA, isNot(messageB));
expect(messageA.hashCode, isNot(messageB.hashCode));
});
group('doctor', () {
MockPlistParser mockPlistParser;
MemoryFileSystem fileSystem;
......@@ -72,7 +83,8 @@ void main() {
testUsingContext('intellij validator', () async {
const String installPath = '/path/to/intelliJ';
final ValidationResult result = await IntelliJValidatorTestTarget('Test', installPath).validate();
// Uses real filesystem
final ValidationResult result = await IntelliJValidatorTestTarget('Test', installPath, fileSystem: globals.fs).validate();
expect(result.type, ValidationType.partial);
expect(result.statusInfo, 'version test.test.test');
expect(result.messages, hasLength(4));
......@@ -96,7 +108,7 @@ void main() {
final Directory pluginsDirectory = fileSystem.directory('/foo/bar/Library/Application Support/JetBrains/TestID2020.10/plugins')
..createSync(recursive: true);
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/path/to/app');
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/path/to/app', fileSystem: fileSystem);
expect(validator.plistFile, '/path/to/app/Contents/Info.plist');
expect(validator.pluginsPath, pluginsDirectory.path);
}, overrides: <Type, Generator>{
......@@ -113,7 +125,7 @@ void main() {
testUsingContext('legacy intellij plugins path checking on mac', () async {
when(mockPlistParser.getValueFromFile(any, PlistParser.kCFBundleShortVersionStringKey)).thenReturn('2020.10');
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/foo');
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/foo', fileSystem: fileSystem);
expect(validator.pluginsPath, '/foo/bar/Library/Application Support/TestID2020.10');
}, overrides: <Type, Generator>{
Platform: () => macPlatform,
......@@ -129,7 +141,7 @@ void main() {
testUsingContext('intellij plugins path checking on mac with override', () async {
when(mockPlistParser.getValueFromFile(any, 'JetBrainsToolboxApp')).thenReturn('/path/to/JetBrainsToolboxApp');
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/foo');
final IntelliJValidatorOnMac validator = IntelliJValidatorOnMac('Test', 'TestID', '/foo', fileSystem: fileSystem);
expect(validator.pluginsPath, '/path/to/JetBrainsToolboxApp.plugins');
}, overrides: <Type, Generator>{
PlistParser: () => mockPlistParser,
......@@ -873,8 +885,10 @@ class NoOpDoctor implements Doctor {
class MockUsage extends Mock implements Usage {}
class IntelliJValidatorTestTarget extends IntelliJValidator {
IntelliJValidatorTestTarget(String title, String installPath) : super(title, installPath);
IntelliJValidatorTestTarget(String title, String installPath, {@required FileSystem fileSystem})
: super(title, installPath, fileSystem: fileSystem);
// Warning: requires real test data.
@override
String get pluginsPath => globals.fs.path.join('test', 'data', 'intellij', 'plugins');
......
......@@ -4,9 +4,12 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_studio_validator.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:mockito/mockito.dart';
......@@ -29,13 +32,19 @@ void main() {
fileSystem = MemoryFileSystem.test();
});
group('NoAndroidStudioValidator', () {
testUsingContext('shows Android Studio as "not available" when not available.', () async {
final NoAndroidStudioValidator validator = NoAndroidStudioValidator();
testWithoutContext('NoAndroidStudioValidator shows Android Studio as "not available" when not available.', () async {
final Config config = Config.test(
'test',
directory: fileSystem.currentDirectory,
logger: BufferLogger.test(),
);
final NoAndroidStudioValidator validator = NoAndroidStudioValidator(
config: config,
platform: linuxPlatform,
userMessages: UserMessages(),
);
expect((await validator.validate()).type, equals(ValidationType.notAvailable));
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
});
});
testUsingContext('AndroidStudioValidator gives doctor error on java crash', () async {
......@@ -53,7 +62,7 @@ void main() {
// This checks that running the validator doesn't throw an unhandled
// exception and that the ProcessException makes it into the error
// message list.
for (final DoctorValidator validator in AndroidStudioValidator.allValidators) {
for (final DoctorValidator validator in AndroidStudioValidator.allValidators(globals.config, globals.platform, globals.fs, globals.userMessages)) {
final ValidationResult result = await validator.validate();
expect(result.messages.where((ValidationMessage message) {
return message.isError && message.message.contains('ProcessException');
......
......@@ -10,56 +10,54 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/intellij/intellij.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
FileSystem fs;
FileSystem fileSystem;
void writeFileCreatingDirectories(String path, List<int> bytes) {
final File file = globals.fs.file(path);
final File file = fileSystem.file(path);
file.parent.createSync(recursive: true);
file.writeAsBytesSync(bytes);
}
setUp(() {
fs = MemoryFileSystem();
fileSystem = MemoryFileSystem.test();
});
group('IntelliJ', () {
group('plugins', () {
testUsingContext('found', () async {
final IntelliJPlugins plugins = IntelliJPlugins(_kPluginsPath);
testWithoutContext('IntelliJPlugins found', () async {
final IntelliJPlugins plugins = IntelliJPlugins(_kPluginsPath, fileSystem: fileSystem);
final Archive dartJarArchive =
buildSingleFileArchive('META-INF/plugin.xml', r'''
<idea-plugin version="2">
<name>Dart</name>
<version>162.2485</version>
<name>Dart</name>
<version>162.2485</version>
</idea-plugin>
''');
writeFileCreatingDirectories(
globals.fs.path.join(_kPluginsPath, 'Dart', 'lib', 'Dart.jar'),
ZipEncoder().encode(dartJarArchive));
fileSystem.path.join(_kPluginsPath, 'Dart', 'lib', 'Dart.jar'),
ZipEncoder().encode(dartJarArchive),
);
final Archive flutterJarArchive =
buildSingleFileArchive('META-INF/plugin.xml', r'''
final Archive flutterJarArchive = buildSingleFileArchive('META-INF/plugin.xml', r'''
<idea-plugin version="2">
<name>Flutter</name>
<version>0.1.3</version>
<name>Flutter</name>
<version>0.1.3</version>
</idea-plugin>
''');
writeFileCreatingDirectories(
globals.fs.path.join(_kPluginsPath, 'flutter-intellij.jar'),
ZipEncoder().encode(flutterJarArchive));
fileSystem.path.join(_kPluginsPath, 'flutter-intellij.jar'),
ZipEncoder().encode(flutterJarArchive),
);
final List<ValidationMessage> messages = <ValidationMessage>[];
plugins.validatePackage(messages, <String>['Dart'], 'Dart');
plugins.validatePackage(messages, <String>['Dart'], 'Dart', 'download-Dart');
plugins.validatePackage(messages,
<String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter',
minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
<String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter', 'download-Flutter',
minVersion: IntelliJPlugins.kMinFlutterPluginVersion,
);
ValidationMessage message = messages
.firstWhere((ValidationMessage m) => m.message.startsWith('Dart '));
......@@ -69,32 +67,27 @@ void main() {
(ValidationMessage m) => m.message.startsWith('Flutter '));
expect(message.message, contains('Flutter plugin version 0.1.3'));
expect(message.message, contains('recommended minimum version'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('not found', () async {
final IntelliJPlugins plugins = IntelliJPlugins(_kPluginsPath);
testWithoutContext('IntelliJPlugins not found displays a link to their download site', () async {
final IntelliJPlugins plugins = IntelliJPlugins(_kPluginsPath, fileSystem: fileSystem);
final List<ValidationMessage> messages = <ValidationMessage>[];
plugins.validatePackage(messages, <String>['Dart'], 'Dart');
plugins.validatePackage(messages, <String>['Dart'], 'Dart', 'download-Dart');
plugins.validatePackage(messages,
<String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter',
minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
<String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter', 'download-Flutter',
minVersion: IntelliJPlugins.kMinFlutterPluginVersion,
);
ValidationMessage message = messages
.firstWhere((ValidationMessage m) => m.message.startsWith('Dart '));
expect(message.message, contains('Dart plugin not installed'));
expect(message.message, contains('Dart plugin can be installed from'));
expect(message.contextUrl, isNotNull);
message = messages.firstWhere(
(ValidationMessage m) => m.message.startsWith('Flutter '));
expect(message.message, contains('Flutter plugin not installed'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
});
expect(message.message, contains('Flutter plugin can be installed from'));
expect(message.contextUrl, isNotNull);
});
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment