Skip to content

Commit 21507c8

Browse files
committed
Set up basic end to end test for extract component
1 parent 3506612 commit 21507c8

File tree

2 files changed

+177
-28
lines changed

2 files changed

+177
-28
lines changed

src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs

+177
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using System.Threading;
1111
using System.Threading.Tasks;
12+
using Castle.Core.Logging;
1213
using Microsoft.AspNetCore.Razor.Language;
1314
using Microsoft.AspNetCore.Razor.Language.Components;
1415
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
@@ -38,6 +39,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
3839
public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServerDelegatingEndpointTestBase(testOutput)
3940
{
4041
private const string GenerateEventHandlerTitle = "Generate Event Handler 'DoesNotExist'";
42+
private const string ExtractToComponentTitle = "Extract element to new component";
4143
private const string GenerateAsyncEventHandlerTitle = "Generate Async Event Handler 'DoesNotExist'";
4244
private const string GenerateEventHandlerReturnType = "void";
4345
private const string GenerateAsyncEventHandlerReturnType = "global::System.Threading.Tasks.Task";
@@ -59,6 +61,17 @@ private GenerateMethodCodeActionResolver[] CreateRazorCodeActionResolvers(
5961
razorFormattingService)
6062
];
6163

64+
// TODO: Make this func
65+
private ExtractToComponentCodeActionResolver[] CreateExtractComponentCodeActionResolvers(string filePath, RazorCodeDocument codeDocument)
66+
{
67+
var emptyDocumentContextFactory = new TestDocumentContextFactory();
68+
return [
69+
new ExtractToComponentCodeActionResolver(
70+
new GenerateMethodResolverDocumentContextFactory(filePath, codeDocument),
71+
TestLanguageServerFeatureOptions.Instance)
72+
];
73+
}
74+
6275
#region CSharp CodeAction Tests
6376

6477
[Fact]
@@ -1005,6 +1018,36 @@ await ValidateCodeActionAsync(input,
10051018
diagnostics: [new Diagnostic() { Code = "CS0103", Message = "The name 'DoesNotExist' does not exist in the current context" }]);
10061019
}
10071020

1021+
[Fact]
1022+
public async Task Handle_ExtractComponent()
1023+
{
1024+
var input = """
1025+
<[||]div id="a">
1026+
<h1>Div a title</h1>
1027+
<Book Title="To Kill a Mockingbird" Author="Harper Lee" Year="Long ago" />
1028+
<p>Div a par</p>
1029+
</div>
1030+
<div id="shouldSkip">
1031+
<Movie Title="Aftersun" Director="Charlotte Wells" Year="2022" />
1032+
</div>
1033+
""";
1034+
1035+
var expectedRazorComponent = """
1036+
<div id="a">
1037+
<h1>Div a title</h1>
1038+
<Book Title="To Kill a Mockingbird" Author="Harper Lee" Year="Long ago" />
1039+
<p>Div a par</p>
1040+
</div>
1041+
""";
1042+
1043+
await ValidateExtractComponentCodeActionAsync(
1044+
input,
1045+
expectedRazorComponent,
1046+
ExtractToComponentTitle,
1047+
razorCodeActionProviders: [new ExtractToComponentCodeActionProvider(LoggerFactory)],
1048+
codeActionResolversCreator: CreateExtractComponentCodeActionResolvers);
1049+
}
1050+
10081051
#endregion
10091052

10101053
private async Task ValidateCodeBehindFileAsync(
@@ -1148,6 +1191,66 @@ private async Task ValidateCodeActionAsync(
11481191
AssertEx.EqualOrDiff(expected, actual);
11491192
}
11501193

1194+
private async Task ValidateExtractComponentCodeActionAsync(
1195+
string input,
1196+
string? expected,
1197+
string codeAction,
1198+
int childActionIndex = 0,
1199+
IRazorCodeActionProvider[]? razorCodeActionProviders = null,
1200+
Func<string, RazorCodeDocument, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
1201+
RazorLSPOptionsMonitor? optionsMonitor = null,
1202+
Diagnostic[]? diagnostics = null)
1203+
{
1204+
TestFileMarkupParser.GetSpan(input, out input, out var textSpan);
1205+
1206+
var razorFilePath = "C:/path/test.razor";
1207+
var componentFilePath = "C:/path/Component.razor";
1208+
var codeDocument = CreateCodeDocument(input, filePath: razorFilePath);
1209+
var sourceText = codeDocument.GetSourceText();
1210+
var uri = new Uri(razorFilePath);
1211+
var languageServer = await CreateLanguageServerAsync(codeDocument, razorFilePath);
1212+
var documentContext = CreateDocumentContext(uri, codeDocument);
1213+
var requestContext = new RazorRequestContext(documentContext, null!, "lsp/method", uri: null);
1214+
1215+
var result = await GetCodeActionsAsync(
1216+
uri,
1217+
textSpan,
1218+
sourceText,
1219+
requestContext,
1220+
languageServer,
1221+
razorCodeActionProviders,
1222+
diagnostics);
1223+
1224+
Assert.NotEmpty(result);
1225+
var codeActionToRun = GetCodeActionToRun(codeAction, childActionIndex, result);
1226+
1227+
if (expected is null)
1228+
{
1229+
Assert.Null(codeActionToRun);
1230+
return;
1231+
}
1232+
1233+
Assert.NotNull(codeActionToRun);
1234+
1235+
var formattingService = await TestRazorFormattingService.CreateWithFullSupportAsync(LoggerFactory, codeDocument, documentContext.Snapshot, optionsMonitor?.CurrentValue);
1236+
var changes = await GetEditsAsync(
1237+
codeActionToRun,
1238+
requestContext,
1239+
languageServer,
1240+
codeActionResolversCreator?.Invoke(razorFilePath, codeDocument) ?? []);
1241+
1242+
var edits = new List<TextChange>();
1243+
1244+
// Only get changes made in the new component file
1245+
foreach (var change in changes.Where(e => e.TextDocument.Uri.AbsolutePath == componentFilePath))
1246+
{
1247+
edits.AddRange(change.Edits.Select(e => e.ToTextChange(sourceText)));
1248+
}
1249+
1250+
var actual = sourceText.WithChanges(edits).ToString();
1251+
AssertEx.EqualOrDiff(expected, actual);
1252+
}
1253+
11511254
private static VSInternalCodeAction? GetCodeActionToRun(string codeAction, int childActionIndex, SumType<Command, CodeAction>[] result)
11521255
{
11531256
var codeActionToRun = (VSInternalCodeAction?)result.SingleOrDefault(e => ((RazorVSInternalCodeAction)e.Value!).Name == codeAction || ((RazorVSInternalCodeAction)e.Value!).Title == codeAction).Value;
@@ -1306,4 +1409,78 @@ static IEnumerable<TagHelperDescriptor> BuildTagHelpers()
13061409
}
13071410
}
13081411
}
1412+
1413+
private class ExtractToComponentResolverDocumentContextFactory : TestDocumentContextFactory
1414+
{
1415+
private readonly List<TagHelperDescriptor> _tagHelperDescriptors;
1416+
1417+
public ExtractToComponentResolverDocumentContextFactory
1418+
(string filePath,
1419+
RazorCodeDocument codeDocument,
1420+
TagHelperDescriptor[]? tagHelpers = null,
1421+
int? version = null)
1422+
: base(filePath, codeDocument, version)
1423+
{
1424+
_tagHelperDescriptors = CreateTagHelperDescriptors();
1425+
if (tagHelpers is not null)
1426+
{
1427+
_tagHelperDescriptors.AddRange(tagHelpers);
1428+
}
1429+
}
1430+
1431+
public override bool TryCreate(
1432+
Uri documentUri,
1433+
VSProjectContext? projectContext,
1434+
bool versioned,
1435+
[NotNullWhen(true)] out DocumentContext? context)
1436+
{
1437+
if (FilePath is null || CodeDocument is null)
1438+
{
1439+
context = null;
1440+
return false;
1441+
}
1442+
1443+
var projectWorkspaceState = ProjectWorkspaceState.Create(_tagHelperDescriptors.ToImmutableArray());
1444+
var testDocumentSnapshot = TestDocumentSnapshot.Create(FilePath, CodeDocument.GetSourceText().ToString(), CodeAnalysis.VersionStamp.Default, projectWorkspaceState);
1445+
testDocumentSnapshot.With(CodeDocument);
1446+
1447+
context = CreateDocumentContext(new Uri(FilePath), testDocumentSnapshot);
1448+
return true;
1449+
}
1450+
1451+
private static List<TagHelperDescriptor> CreateTagHelperDescriptors()
1452+
{
1453+
return BuildTagHelpers().ToList();
1454+
1455+
static IEnumerable<TagHelperDescriptor> BuildTagHelpers()
1456+
{
1457+
var builder = TagHelperDescriptorBuilder.Create("oncontextmenu", "Microsoft.AspNetCore.Components");
1458+
builder.SetMetadata(
1459+
new KeyValuePair<string, string>(ComponentMetadata.EventHandler.EventArgsType, "Microsoft.AspNetCore.Components.Web.MouseEventArgs"),
1460+
new KeyValuePair<string, string>(ComponentMetadata.SpecialKindKey, ComponentMetadata.EventHandler.TagHelperKind));
1461+
yield return builder.Build();
1462+
1463+
builder = TagHelperDescriptorBuilder.Create("onclick", "Microsoft.AspNetCore.Components");
1464+
builder.SetMetadata(
1465+
new KeyValuePair<string, string>(ComponentMetadata.EventHandler.EventArgsType, "Microsoft.AspNetCore.Components.Web.MouseEventArgs"),
1466+
new KeyValuePair<string, string>(ComponentMetadata.SpecialKindKey, ComponentMetadata.EventHandler.TagHelperKind));
1467+
1468+
yield return builder.Build();
1469+
1470+
builder = TagHelperDescriptorBuilder.Create("oncopy", "Microsoft.AspNetCore.Components");
1471+
builder.SetMetadata(
1472+
new KeyValuePair<string, string>(ComponentMetadata.EventHandler.EventArgsType, "Microsoft.AspNetCore.Components.Web.ClipboardEventArgs"),
1473+
new KeyValuePair<string, string>(ComponentMetadata.SpecialKindKey, ComponentMetadata.EventHandler.TagHelperKind));
1474+
1475+
yield return builder.Build();
1476+
1477+
builder = TagHelperDescriptorBuilder.Create("ref", "Microsoft.AspNetCore.Components");
1478+
builder.SetMetadata(
1479+
new KeyValuePair<string, string>(ComponentMetadata.SpecialKindKey, ComponentMetadata.Ref.TagHelperKind),
1480+
new KeyValuePair<string, string>(ComponentMetadata.Common.DirectiveAttribute, bool.TrueString));
1481+
1482+
yield return builder.Build();
1483+
}
1484+
}
1485+
}
13091486
}

src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/Razor/ExtractToComponentCodeActionProviderTest.cs

-28
Original file line numberDiff line numberDiff line change
@@ -365,34 +365,6 @@ private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionP
365365
return context;
366366
}
367367

368-
//private static IDocumentSnapshot CreateSupplementaryRazorFile(string filePath, string text, bool supportsFileCreation = true)
369-
// => CreateSupplementaryRazorFile(filePath, text, relativePath: filePath, supportsFileCreation: supportsFileCreation);
370-
371-
//private static IDocumentSnapshot CreateSupplementaryRazorFile(string filePath, string text, string? relativePath, bool supportsFileCreation = true)
372-
//{
373-
// var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create(filePath, relativePath));
374-
// var options = RazorParserOptions.Create(o =>
375-
// {
376-
// o.Directives.Add(ComponentCodeDirective.Directive);
377-
// o.Directives.Add(FunctionsDirective.Directive);
378-
// });
379-
// var syntaxTree = RazorSyntaxTree.Parse(sourceDocument, options);
380-
381-
// var codeDocument = TestRazorCodeDocument.Create(sourceDocument, imports: default);
382-
// codeDocument.SetFileKind(FileKinds.Component);
383-
// codeDocument.SetCodeGenerationOptions(RazorCodeGenerationOptions.Create(o =>
384-
// {
385-
// o.RootNamespace = "ExtractToNewComponentTest";
386-
// }));
387-
// codeDocument.SetSyntaxTree(syntaxTree);
388-
389-
// var documentSnapshot = Mock.Of<IDocumentSnapshot>(document =>
390-
// document.GetGeneratedOutputAsync() == Task.FromResult(codeDocument) &&
391-
// document.GetTextAsync() == Task.FromResult(codeDocument.GetSourceText()), MockBehavior.Strict);
392-
393-
// return documentSnapshot;
394-
//}
395-
396368
private static void AddMultiPointSelectionToContext(ref RazorCodeActionContext context, TextSpan selectionSpan)
397369
{
398370
var sourceText = context.CodeDocument.GetSourceText();

0 commit comments

Comments
 (0)