85c28b804d
Change-Id: I88af71be9788bd417a1f739f0f3e0efe930c2cd0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103842 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Danny Tuppeny <dantup@google.com>
473 lines
15 KiB
Dart
473 lines
15 KiB
Dart
// Copyright (c) 2018, 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 'package:analysis_server/lsp_protocol/protocol_generated.dart';
|
|
import 'package:analysis_server/src/lsp/constants.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import '../tool/lsp_spec/matchers.dart';
|
|
import 'code_actions_abstract.dart';
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(SortMembersSourceCodeActionsTest);
|
|
defineReflectiveTests(OrganizeImportsSourceCodeActionsTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class OrganizeImportsSourceCodeActionsTest extends AbstractCodeActionsTest {
|
|
test_appliesCorrectEdits_withDocumentChangesSupport() async {
|
|
const content = '''
|
|
import 'dart:math';
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
Future foo;
|
|
int minified(int x, int y) => min(x, y);
|
|
''';
|
|
const expectedContent = '''
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
|
|
Future foo;
|
|
int minified(int x, int y) => min(x, y);
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities: withApplyEditSupport(
|
|
withDocumentChangesSupport(emptyWorkspaceClientCapabilities)));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
ApplyWorkspaceEditParams editParams;
|
|
|
|
final commandResponse = await handleExpectedRequest<Object,
|
|
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
|
|
Method.workspace_applyEdit,
|
|
() => executeCommand(command),
|
|
handler: (edit) {
|
|
// When the server sends the edit back, just keep a copy and say we
|
|
// applied successfully (it'll be verified below).
|
|
editParams = edit;
|
|
return new ApplyWorkspaceEditResponse(true, null);
|
|
},
|
|
);
|
|
// Successful edits return an empty success() response.
|
|
expect(commandResponse, isNull);
|
|
|
|
// Ensure the edit came back, and using the documentChanges.
|
|
expect(editParams, isNotNull);
|
|
expect(editParams.edit.documentChanges, isNotNull);
|
|
expect(editParams.edit.changes, isNull);
|
|
|
|
// Ensure applying the changes will give us the expected content.
|
|
final contents = {
|
|
mainFilePath: withoutMarkers(content),
|
|
};
|
|
applyDocumentChanges(contents, editParams.edit.documentChanges);
|
|
expect(contents[mainFilePath], equals(expectedContent));
|
|
}
|
|
|
|
test_appliesCorrectEdits_withoutDocumentChangesSupport() async {
|
|
const content = '''
|
|
import 'dart:math';
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
Future foo;
|
|
int minified(int x, int y) => min(x, y);
|
|
''';
|
|
const expectedContent = '''
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
|
|
Future foo;
|
|
int minified(int x, int y) => min(x, y);
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
ApplyWorkspaceEditParams editParams;
|
|
|
|
final commandResponse = await handleExpectedRequest<Object,
|
|
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
|
|
Method.workspace_applyEdit,
|
|
() => executeCommand(command),
|
|
handler: (edit) {
|
|
// When the server sends the edit back, just keep a copy and say we
|
|
// applied successfully (it'll be verified below).
|
|
editParams = edit;
|
|
return new ApplyWorkspaceEditResponse(true, null);
|
|
},
|
|
);
|
|
// Successful edits return an empty success() response.
|
|
expect(commandResponse, isNull);
|
|
|
|
// Ensure the edit came back, and using changes.
|
|
expect(editParams, isNotNull);
|
|
expect(editParams.edit.changes, isNotNull);
|
|
expect(editParams.edit.documentChanges, isNull);
|
|
|
|
// Ensure applying the changes will give us the expected content.
|
|
final contents = {
|
|
mainFilePath: withoutMarkers(content),
|
|
};
|
|
applyChanges(contents, editParams.edit.changes);
|
|
expect(contents[mainFilePath], equals(expectedContent));
|
|
}
|
|
|
|
test_availableAsCodeActionLiteral() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
textDocumentCapabilities: withCodeActionKinds(
|
|
emptyTextDocumentClientCapabilities, [CodeActionKind.Source]),
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
await checkCodeActionAvailable(
|
|
mainFileUri,
|
|
Commands.organizeImports,
|
|
'Organize Imports',
|
|
asCodeActionLiteral: true,
|
|
);
|
|
}
|
|
|
|
test_availableAsCommand() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
await checkCodeActionAvailable(
|
|
mainFileUri,
|
|
Commands.organizeImports,
|
|
'Organize Imports',
|
|
asCommand: true,
|
|
);
|
|
}
|
|
|
|
test_failsIfFileHasErrors() async {
|
|
final content = 'invalid dart code';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
// Ensure the request returned an error (error repsonses are thrown by
|
|
// the test helper to make consuming success results simpler).
|
|
await expectLater(executeCommand(command),
|
|
throwsA(isResponseError(ServerErrorCodes.FileHasErrors)));
|
|
}
|
|
|
|
test_noEdits() async {
|
|
const content = '''
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
|
|
Future foo;
|
|
int minified(int x, int y) => min(x, y);
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
// Execute the command and it should return without needing us to process
|
|
// a workspace/applyEdit command because there were no edits.
|
|
final commandResponse = await executeCommand(command);
|
|
// Successful edits return an empty success() response.
|
|
expect(commandResponse, isNull);
|
|
}
|
|
|
|
test_unavailableWhenNotRequested() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
textDocumentCapabilities: withCodeActionKinds(
|
|
emptyTextDocumentClientCapabilities, [CodeActionKind.Refactor]),
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNull);
|
|
}
|
|
|
|
test_unavailableWithoutApplyEditSupport() async {
|
|
await newFile(mainFilePath);
|
|
await initialize();
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.organizeImports);
|
|
expect(codeAction, isNull);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class SortMembersSourceCodeActionsTest extends AbstractCodeActionsTest {
|
|
test_appliesCorrectEdits_withDocumentChangesSupport() async {
|
|
const content = '''
|
|
String b;
|
|
String a;
|
|
''';
|
|
const expectedContent = '''
|
|
String a;
|
|
String b;
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities: withApplyEditSupport(
|
|
withDocumentChangesSupport(emptyWorkspaceClientCapabilities)));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
ApplyWorkspaceEditParams editParams;
|
|
|
|
final commandResponse = await handleExpectedRequest<Object,
|
|
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
|
|
Method.workspace_applyEdit,
|
|
() => executeCommand(command),
|
|
handler: (edit) {
|
|
// When the server sends the edit back, just keep a copy and say we
|
|
// applied successfully (it'll be verified below).
|
|
editParams = edit;
|
|
return new ApplyWorkspaceEditResponse(true, null);
|
|
},
|
|
);
|
|
// Successful edits return an empty success() response.
|
|
expect(commandResponse, isNull);
|
|
|
|
// Ensure the edit came back, and using the documentChanges.
|
|
expect(editParams, isNotNull);
|
|
expect(editParams.edit.documentChanges, isNotNull);
|
|
expect(editParams.edit.changes, isNull);
|
|
|
|
// Ensure applying the changes will give us the expected content.
|
|
final contents = {
|
|
mainFilePath: withoutMarkers(content),
|
|
};
|
|
applyDocumentChanges(contents, editParams.edit.documentChanges);
|
|
expect(contents[mainFilePath], equals(expectedContent));
|
|
}
|
|
|
|
test_appliesCorrectEdits_withoutDocumentChangesSupport() async {
|
|
const content = '''
|
|
String b;
|
|
String a;
|
|
''';
|
|
const expectedContent = '''
|
|
String a;
|
|
String b;
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
ApplyWorkspaceEditParams editParams;
|
|
|
|
final commandResponse = await handleExpectedRequest<Object,
|
|
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
|
|
Method.workspace_applyEdit,
|
|
() => executeCommand(command),
|
|
handler: (edit) {
|
|
// When the server sends the edit back, just keep a copy and say we
|
|
// applied successfully (it'll be verified below).
|
|
editParams = edit;
|
|
return new ApplyWorkspaceEditResponse(true, null);
|
|
},
|
|
);
|
|
// Successful edits return an empty success() response.
|
|
expect(commandResponse, isNull);
|
|
|
|
// Ensure the edit came back, and using changes.
|
|
expect(editParams, isNotNull);
|
|
expect(editParams.edit.changes, isNotNull);
|
|
expect(editParams.edit.documentChanges, isNull);
|
|
|
|
// Ensure applying the changes will give us the expected content.
|
|
final contents = {
|
|
mainFilePath: withoutMarkers(content),
|
|
};
|
|
applyChanges(contents, editParams.edit.changes);
|
|
expect(contents[mainFilePath], equals(expectedContent));
|
|
}
|
|
|
|
test_availableAsCodeActionLiteral() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
textDocumentCapabilities: withCodeActionKinds(
|
|
emptyTextDocumentClientCapabilities, [CodeActionKind.Source]),
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
await checkCodeActionAvailable(
|
|
mainFileUri,
|
|
Commands.sortMembers,
|
|
'Sort Members',
|
|
asCodeActionLiteral: true,
|
|
);
|
|
}
|
|
|
|
test_availableAsCommand() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
await checkCodeActionAvailable(
|
|
mainFileUri,
|
|
Commands.sortMembers,
|
|
'Sort Members',
|
|
asCommand: true,
|
|
);
|
|
}
|
|
|
|
test_failsIfClientDoesntApplyEdits() async {
|
|
const content = '''
|
|
String b;
|
|
String a;
|
|
''';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
final commandResponse = handleExpectedRequest<Object,
|
|
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
|
|
Method.workspace_applyEdit,
|
|
() => executeCommand(command),
|
|
// Claim that we failed tpo apply the edits. This is what the client
|
|
// would do if the edits provided were for an old version of the
|
|
// document.
|
|
handler: (edit) =>
|
|
new ApplyWorkspaceEditResponse(false, 'Document changed'),
|
|
);
|
|
|
|
// Ensure the request returned an error (error repsonses are thrown by
|
|
// the test helper to make consuming success results simpler).
|
|
await expectLater(commandResponse,
|
|
throwsA(isResponseError(ServerErrorCodes.ClientFailedToApplyEdit)));
|
|
}
|
|
|
|
test_failsIfFileHasErrors() async {
|
|
final content = 'invalid dart code';
|
|
await newFile(mainFilePath, content: content);
|
|
await initialize(
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNotNull);
|
|
|
|
final command = codeAction.map(
|
|
(command) => command,
|
|
(codeAction) => codeAction.command,
|
|
);
|
|
|
|
// Ensure the request returned an error (error repsonses are thrown by
|
|
// the test helper to make consuming success results simpler).
|
|
await expectLater(executeCommand(command),
|
|
throwsA(isResponseError(ServerErrorCodes.FileHasErrors)));
|
|
}
|
|
|
|
test_nonDartFile() async {
|
|
await newFile(pubspecFilePath, content: simplePubspecContent);
|
|
await initialize(
|
|
textDocumentCapabilities: withCodeActionKinds(
|
|
emptyTextDocumentClientCapabilities, [CodeActionKind.Source]),
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions =
|
|
await getCodeActions(pubspecFileUri.toString(), range: startOfDocRange);
|
|
expect(codeActions, isEmpty);
|
|
}
|
|
|
|
test_unavailableWhenNotRequested() async {
|
|
await newFile(mainFilePath);
|
|
await initialize(
|
|
textDocumentCapabilities: withCodeActionKinds(
|
|
emptyTextDocumentClientCapabilities, [CodeActionKind.Refactor]),
|
|
workspaceCapabilities:
|
|
withApplyEditSupport(emptyWorkspaceClientCapabilities));
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNull);
|
|
}
|
|
|
|
test_unavailableWithoutApplyEditSupport() async {
|
|
await newFile(mainFilePath);
|
|
await initialize();
|
|
|
|
final codeActions = await getCodeActions(mainFileUri.toString());
|
|
final codeAction = findCommand(codeActions, Commands.sortMembers);
|
|
expect(codeAction, isNull);
|
|
}
|
|
}
|