blob: 1298fc0e8e83c5a88894d03c5aac16db33099845 [file] [log] [blame]
// Copyright (c) 2025, 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:async';
import 'package:analysis_server/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analyzer/src/test_utilities/platform.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:test/test.dart';
import '../lsp/request_helpers_mixin.dart';
import '../lsp/server_abstract.dart';
import '../tool/lsp_spec/matchers.dart';
import '../utils/test_code_extensions.dart';
import 'shared_test_interface.dart';
/// Shared edit argument tests that are used by both LSP + Legacy server
/// tests.
mixin SharedEditArgumentTests
on
SharedTestInterface,
LspRequestHelpersMixin,
LspVerifyEditHelpersMixin,
ClientCapabilitiesHelperMixin {
late TestCode code;
@override
Future<void> setUp() async {
await super.setUp();
setApplyEditSupport();
setDocumentChangesSupport();
}
Future<void> test_comma_addArg_addsIfExists() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '(x: 1,)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2,)',
);
}
Future<void> test_comma_addArg_doesNotAddIfNotExists() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2)',
);
}
Future<void> test_comma_editArg_doesNotAddIfNotExists() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '(x: 1, y: 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2)',
);
}
Future<void> test_comma_editArg_retainsIfExists() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '(x: 1, y: 1,)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2,)',
);
}
Future<void> test_documentChanges_supported() async {
// Ensure documentChanges are supported. The verification in
// LspChangeVerifier will verify the resulting edits match the capabilities.
setDocumentChangesSupport();
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_documentChanges_unsupported() async {
// documentChanges are NOT supported. The verification in
// LspChangeVerifier will verify the resulting edits match the capabilities.
setDocumentChangesSupport(false);
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_format_multiline_insert_between() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y, int? children })',
originalArgs: '''\n
(
x: 1,
children: 3,
)''',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '''\n
(
x: 1,
y: 2,
children: 3,
)''',
);
}
Future<void> test_format_multiline_insert_last() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '''\n
(
x: 1,
)''',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '''\n
(
x: 1,
y: 2,
)''',
);
}
Future<void> test_format_multiline_insert_solo() async {
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '''\n
(
)''',
edit: ArgumentEdit(name: 'x', newValue: 1),
expectedArgs: '''\n
(
x: 1,
)''',
);
}
Future<void> test_named_addAfterNamed() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2)',
);
}
Future<void> test_named_addAfterNamed_afterChildNotAtEnd() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y, Widget? child })',
originalArgs: '(child: null, x: 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(child: null, x: 1, y: 2)',
);
}
Future<void> test_named_addAfterNamed_beforeChild_noOthers() async {
await _expectSimpleArgumentEdit(
params: '({ int? y, Widget? child })',
originalArgs: '(child: null)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(y: 2, child: null)',
);
}
Future<void> test_named_addAfterNamed_beforeChild_noOthers_multiline() async {
await _expectSimpleArgumentEdit(
params: '({ int? y, Widget? child })',
originalArgs: '''\n
(
child: null
)''',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '''\n
(
y: 2,
child: null
)''',
);
}
Future<void> test_named_addAfterNamed_beforeChildAtEnd() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y, Widget? child })',
originalArgs: '(x: 1, child: null)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2, child: null)',
);
}
Future<void> test_named_addAfterNamed_beforeChildren_noOthers() async {
await _expectSimpleArgumentEdit(
params: '({ int? y, List<Widget>? children })',
originalArgs: '(children: [])',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(y: 2, children: [])',
);
}
Future<void>
test_named_addAfterNamed_beforeChildren_noOthers_multiline() async {
await _expectSimpleArgumentEdit(
params: '({ int? y, List<Widget>? children })',
originalArgs: '''\n
(
children: []
)''',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '''\n
(
y: 2,
children: []
)''',
);
}
Future<void> test_named_addAfterNamed_beforeChildrenAtEnd() async {
await _expectSimpleArgumentEdit(
params: '({ int? x, int? y, List<Widget>? children })',
originalArgs: '(x: 1, children: [])',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(x: 1, y: 2, children: [])',
);
}
Future<void> test_named_addAfterPositional() async {
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y })',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(1, y: 2)',
);
}
Future<void> test_named_addAfterPositional_afterChildNotAtEnd() async {
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y, Widget? child })',
originalArgs: '(child: null, 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(child: null, 1, y: 2)',
);
}
Future<void> test_named_addAfterPositional_beforeChildAtEnd() async {
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y, Widget? child })',
originalArgs: '(1, child: null)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(1, y: 2, child: null)',
);
}
Future<void> test_named_addAfterPositional_beforeChildrenAtEnd() async {
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y, List<Widget>? children })',
originalArgs: '(1, children: [])',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(1, y: 2, children: [])',
);
}
Future<void> test_optionalPositional_addAfterPositional() async {
await _expectSimpleArgumentEdit(
params: '([int? x, int? y])',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(1, 2)',
);
}
Future<void> test_optionalPositional_notNext_afterPositional() async {
await _expectFailedEdit(
params: '([int? x, int y = 10, int? z])',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'z', newValue: 2),
errorCode: ServerErrorCodes.EditArgumentInvalidParameter,
message:
"The parameter 'z' is not editable because "
"a value for the 3rd parameter can't be added until a value for all preceding positional parameters have been added.",
);
}
Future<void> test_optionalPositional_notNext_solo() async {
await _expectFailedEdit(
params: '([int? x = 10, int? y])',
originalArgs: '()',
edit: ArgumentEdit(name: 'y', newValue: 2),
errorCode: ServerErrorCodes.EditArgumentInvalidParameter,
message:
"The parameter 'y' is not editable because "
"a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.",
);
}
Future<void> test_requiredPositional_addAfterNamed() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectSimpleArgumentEdit(
params: '(int? x, { int? y })',
originalArgs: '(y: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(y: 1, 2)',
);
}
Future<void> test_requiredPositional_addAfterPositional() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectSimpleArgumentEdit(
params: '(int? x, int? y)',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
expectedArgs: '(1, 2)',
);
}
Future<void> test_requiredPositional_notNext_afterPositional() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectFailedEdit(
params: '(int? x, int? y, int? z)',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'z', newValue: 2),
errorCode: ServerErrorCodes.EditArgumentInvalidParameter,
message:
"The parameter 'z' is not editable because "
"a value for the 3rd parameter can't be added until a value for all preceding positional parameters have been added.",
);
}
Future<void> test_requiredPositional_notNext_noExisting() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectFailedEdit(
params: '(int? x, int? y)',
originalArgs: '()',
edit: ArgumentEdit(name: 'y', newValue: 2),
errorCode: ServerErrorCodes.EditArgumentInvalidParameter,
message:
"The parameter 'y' is not editable because "
"a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.",
);
}
Future<void> test_requiredPositional_notNext_onlyNamed() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectFailedEdit(
params: '(int? x, int? y, { int? z })',
originalArgs: '(z: 1)',
edit: ArgumentEdit(name: 'y', newValue: 2),
errorCode: ServerErrorCodes.EditArgumentInvalidParameter,
message:
"The parameter 'y' is not editable because "
"a value for the 2nd parameter can't be added until a value for all preceding positional parameters have been added.",
);
}
Future<void> test_soloArgument_addNamed() async {
await _expectSimpleArgumentEdit(
params: '({int? x })',
originalArgs: '()',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_soloArgument_addOptionalPositional() async {
await _expectSimpleArgumentEdit(
params: '([int? x])',
originalArgs: '()',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(2)',
);
}
Future<void> test_soloArgument_addRequiredPositional() async {
failTestOnErrorDiagnostic = false; // Tests with missing positional.
await _expectSimpleArgumentEdit(
params: '(int? x)',
originalArgs: '()',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(2)',
);
}
Future<void> test_soloArgument_editNamed() async {
await _expectSimpleArgumentEdit(
params: '({int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_soloArgument_editOptionalPositional() async {
await _expectSimpleArgumentEdit(
params: '([int? x])',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(2)',
);
}
Future<void> test_soloArgument_editRequiredPositional() async {
await _expectSimpleArgumentEdit(
params: '(int? x)',
originalArgs: '(1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(2)',
);
}
Future<void> test_type_bool_invalidType() async {
await _expectFailedEdit(
params: '({ bool? x })',
originalArgs: '(x: true)',
edit: ArgumentEdit(name: 'x', newValue: 'invalid'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' should be bool? but was String",
);
}
Future<void> test_type_bool_null_allowed() async {
await _expectSimpleArgumentEdit(
params: '({ bool? x })',
originalArgs: '(x: true)',
edit: ArgumentEdit(name: 'x'),
expectedArgs: '(x: null)',
);
}
Future<void> test_type_bool_null_notAllowed() async {
await _expectFailedEdit(
params: '({ required bool x })',
originalArgs: '(x: true)',
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' can't be null",
);
}
Future<void> test_type_bool_replaceLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ bool? x })',
originalArgs: '(x: true)',
edit: ArgumentEdit(name: 'x', newValue: false),
expectedArgs: '(x: false)',
);
}
Future<void> test_type_bool_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ bool? x })',
originalArgs: '(x: 1 == 1)',
edit: ArgumentEdit(name: 'x', newValue: false),
expectedArgs: '(x: false)',
);
}
Future<void> test_type_double_invalidType() async {
await _expectFailedEdit(
params: '({ double? x })',
originalArgs: '(x: 1.1)',
edit: ArgumentEdit(name: 'x', newValue: 'invalid'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message:
"The value for the parameter 'x' should be double? but was String",
);
}
Future<void> test_type_double_null_allowed() async {
await _expectSimpleArgumentEdit(
params: '({ double? x })',
originalArgs: '(x: 1.0)',
edit: ArgumentEdit(name: 'x'),
expectedArgs: '(x: null)',
);
}
Future<void> test_type_double_null_notAllowed() async {
await _expectFailedEdit(
params: '({ required double x })',
originalArgs: '(x: 1.0)',
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' can't be null",
);
}
Future<void> test_type_double_replaceInt() async {
await _expectSimpleArgumentEdit(
params: '({ double? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2.2),
expectedArgs: '(x: 2.2)',
);
}
Future<void> test_type_double_replaceLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ double? x })',
originalArgs: '(x: 1.1)',
edit: ArgumentEdit(name: 'x', newValue: 2.2),
expectedArgs: '(x: 2.2)',
);
}
Future<void> test_type_double_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ double? x })',
originalArgs: '(x: 1.1 + 0.1)',
edit: ArgumentEdit(name: 'x', newValue: 2.2),
expectedArgs: '(x: 2.2)',
);
}
Future<void> test_type_double_replaceWithInt() async {
await _expectSimpleArgumentEdit(
params: '({ double? x })',
originalArgs: '(x: 1.1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_type_enum_dotshorthand_addNew() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '()',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: .two)',
);
}
Future<void> test_type_enum_dotshorthand_disabled_addNew() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '()',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: E.two)',
fileComment: '// @dart = 3.8',
);
}
Future<void> test_type_enum_dotshorthand_disabled_replaceLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '(x: E.one)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: E.two)',
fileComment: '// @dart = 3.8',
);
}
Future<void> test_type_enum_dotshorthand_disabled_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: '''
enum E { one, two }
const E myConst = E.one;
''',
params: '({ E? x })',
originalArgs: '(x: myConst)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: E.two)',
fileComment: '// @dart = 3.8',
);
}
Future<void> test_type_enum_dotshorthand_replaceLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '(x: .one)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: .two)',
);
}
Future<void> test_type_enum_dotshorthand_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: '''
enum E { one, two }
const E myConst = .one;
''',
params: '({ E? x })',
originalArgs: '(x: myConst)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: .two)',
);
}
Future<void> test_type_enum_invalidType() async {
await _expectFailedEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '(x: E.one)',
edit: ArgumentEdit(name: 'x', newValue: 'invalid'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message:
"The value for the parameter 'x' should be one of 'E.one', 'E.two' but was 'invalid'",
);
}
Future<void> test_type_enum_null_allowed() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '(x: E.one)',
edit: ArgumentEdit(name: 'x'),
expectedArgs: '(x: null)',
);
}
Future<void> test_type_enum_null_notAllowed() async {
await _expectFailedEdit(
additionalCode: 'enum E { one, two }',
params: '({ required E x })',
originalArgs: '(x: E.one)',
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' can't be null",
);
}
Future<void> test_type_enum_replaceLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: 'enum E { one, two }',
params: '({ E? x })',
originalArgs: '(x: E.one)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: E.two)',
);
}
Future<void> test_type_enum_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
additionalCode: '''
enum E { one, two }
const myConst = E.one;
''',
params: '({ E? x })',
originalArgs: '(x: myConst)',
edit: ArgumentEdit(name: 'x', newValue: 'E.two'),
expectedArgs: '(x: .two)',
);
}
Future<void> test_type_int_invalidType() async {
await _expectFailedEdit(
params: '({ int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 'invalid'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' should be int? but was String",
);
}
Future<void> test_type_int_null_allowed() async {
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x'),
expectedArgs: '(x: null)',
);
}
Future<void> test_type_int_null_notAllowed() async {
await _expectFailedEdit(
params: '({ required int x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' can't be null",
);
}
Future<void> test_type_int_replaceLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '(x: 1)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_type_int_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ int? x })',
originalArgs: '(x: 1 + 0)',
edit: ArgumentEdit(name: 'x', newValue: 2),
expectedArgs: '(x: 2)',
);
}
Future<void> test_type_string_containsBackslashes() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: r'a\b'),
expectedArgs: r"(x: 'a\\b')",
);
}
Future<void> test_type_string_containsBothQuotes() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: '''a'b"c'''),
expectedArgs: r'''(x: 'a\'b"c')''',
);
}
Future<void> test_type_string_containsSingleQuotes() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: "a'b"),
expectedArgs: r'''(x: 'a\'b')''',
);
}
Future<void> test_type_string_invalidType() async {
await _expectFailedEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: 123),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' should be String? but was int",
);
}
Future<void> test_type_string_multiline() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: 'a\nb'),
expectedArgs: r'''(x: 'a\nb')''',
);
}
Future<void> test_type_string_null_allowed() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x'),
expectedArgs: '(x: null)',
);
}
Future<void> test_type_string_null_notAllowed() async {
await _expectFailedEdit(
params: '({ required String x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditArgumentInvalidValue,
message: "The value for the parameter 'x' can't be null",
);
}
Future<void> test_type_string_quotes_dollar_escapedNonRaw() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: '')",
edit: ArgumentEdit(name: 'x', newValue: r'$'),
expectedArgs: r"(x: '\$')",
);
}
Future<void> test_type_string_quotes_dollar_notEscapedRaw() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: r'')",
edit: ArgumentEdit(name: 'x', newValue: r'$'),
expectedArgs: r"(x: r'$')",
);
}
Future<void> test_type_string_quotes_usesExistingDouble() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: '(x: "a")',
edit: ArgumentEdit(name: 'x', newValue: 'a'),
expectedArgs: '(x: "a")',
);
}
Future<void> test_type_string_quotes_usesExistingSingle() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: 'a'),
expectedArgs: "(x: 'a')",
);
}
Future<void> test_type_string_quotes_usesExistingTripleDouble() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: '(x: """a""")',
edit: ArgumentEdit(name: 'x', newValue: 'a'),
expectedArgs: '(x: """a""")',
);
}
Future<void> test_type_string_quotes_usesExistingTripleSingle() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: '''a''')",
edit: ArgumentEdit(name: 'x', newValue: 'a'),
expectedArgs: "(x: '''a''')",
);
}
Future<void> test_type_string_replaceLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x', newValue: 'b'),
expectedArgs: "(x: 'b')",
);
}
Future<void> test_type_string_replaceLiteral_raw() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: r'a')",
edit: ArgumentEdit(name: 'x', newValue: 'b'),
expectedArgs: "(x: r'b')",
);
}
Future<void> test_type_string_replaceLiteral_tripleQuoted() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: '''a''')",
edit: ArgumentEdit(name: 'x', newValue: 'b'),
expectedArgs: "(x: '''b''')",
);
}
Future<void> test_type_string_replaceNonLiteral() async {
await _expectSimpleArgumentEdit(
params: '({ String? x })',
originalArgs: "(x: 'a' + 'a')",
edit: ArgumentEdit(name: 'x', newValue: 'b'),
expectedArgs: "(x: 'b')",
);
}
Future<void> test_unsupported_noApplyEditSupport() async {
setApplyEditSupport(false);
await _expectFailedEdit(
params: '({ required String x })',
originalArgs: "(x: 'a')",
edit: ArgumentEdit(name: 'x'),
errorCode: ServerErrorCodes.EditsUnsupportedByEditor,
message: 'The connected editor does not support applying edits',
);
}
/// Initializes the server with [content] and tries to apply the argument
/// [edit] at the marked location. Verifies the changes made match
/// [expectedContent].
Future<void> _expectArgumentEdit(
String content,
ArgumentEdit edit,
String expectedContent, {
bool open = true,
}) async {
content = normalizeNewlinesForPlatform(content);
expectedContent = normalizeNewlinesForPlatform(expectedContent);
code = TestCode.parse(content);
createFile(testFilePath, code.code);
await initializeServer();
if (open) {
await openFile(testFileUri, code.code);
}
await currentAnalysis;
var verifier = await executeForEdits(
() => editArgument(testFileUri, code.position.position, edit),
);
verifier.verifyFiles(expectedContent);
}
/// Initializes the server and verifies a simple argument edit fails with
/// a given message.
Future<void> _expectFailedEdit({
required String params,
required String originalArgs,
required ArgumentEdit edit,
required ErrorCodes errorCode,
required String message,
String? additionalCode,
}) async {
additionalCode ??= '';
var content = '''
import 'package:flutter/widgets.dart';
$additionalCode
class MyWidget extends StatelessWidget {
const MyWidget$params;
@override
Widget build(BuildContext context) => MyW^idget$originalArgs;
}
''';
code = TestCode.parse(content);
createFile(testFilePath, code.code);
await initializeServer();
await currentAnalysis;
await expectLater(
editArgument(testFileUri, code.position.position, edit),
throwsA(isResponseError(errorCode, message: message)),
);
}
/// Initializes the server and verifies a simple argument edit.
Future<void> _expectSimpleArgumentEdit({
required String params,
required String originalArgs,
required ArgumentEdit edit,
required String expectedArgs,
String? additionalCode = '',
String? fileComment = '',
}) async {
var content = '''
$fileComment
import 'package:flutter/widgets.dart';
$additionalCode
class MyWidget extends StatelessWidget {
const MyWidget$params;
@override
Widget build(BuildContext context) => MyW^idget$originalArgs;
}
''';
var expectedContent = '''
>>>>>>>>>> lib/test.dart
$fileComment
import 'package:flutter/widgets.dart';
$additionalCode
class MyWidget extends StatelessWidget {
const MyWidget$params;
@override
Widget build(BuildContext context) => MyWidget$expectedArgs;
}
''';
await _expectArgumentEdit(content, edit, expectedContent);
}
}