Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/Components/test/E2ETest/Tests/InteropValueTypesTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using BasicTestApp;
using Microsoft.AspNetCore.Components.E2ETest;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using OpenQA.Selenium;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Components.E2ETests.Tests;

public class InteropValueTypesTest(
BrowserFixture browserFixture,
ToggleExecutionModeServerFixture<Program> serverFixture,
ITestOutputHelper output)
: ServerTestBase<ToggleExecutionModeServerFixture<Program>>(browserFixture, serverFixture, output)
{
protected override void InitializeAsyncCore()
{
Navigate(ServerPathBase);
Browser.MountTestComponent<InteropValueTypesComponent>();

var interopButton = Browser.Exists(By.Id("btn-interop"));
interopButton.Click();
Browser.Exists(By.Id("done-with-interop"));
}

[Fact]
public void CanRetrieveStoredGuidAsString()
{
var stringGuidElement = Browser.Exists(By.Id("string-get-by-interop"));

Browser.NotEqual(string.Empty, () => stringGuidElement.Text);
Browser.NotEqual(default, () => Guid.Parse(stringGuidElement.Text));
}

[Fact]
public void CanRetrieveStoredGuidAsGuid()
{
var guidElement = Browser.Exists(By.Id("guid-get-by-interop"));

Browser.NotEqual(default, () => Guid.Parse(guidElement.Text));
}

[Fact]
public void CanRetrieveStoredGuidAsNullableGuid()
{
var nullableGuidElement = Browser.Exists(By.Id("nullable-guid-get-by-interop"));

Browser.NotEqual(default, () => Guid.Parse(nullableGuidElement.Text));
}

[Fact]
public void CanRetrieveNullAsNullableGuid()
{
var nullableGuidElement = Browser.Exists(By.Id("null-loaded-into-nullable"));

Browser.Equal(true.ToString(), () => nullableGuidElement.Text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public StartupErrorNotificationTest(
public void DisplaysNotificationForStartupException(bool errorIsAsync)
{
var url = $"{ServerPathBase}?error={(errorIsAsync ? "async" : "sync")}";

Navigate(url);
var errorUiElem = Browser.Exists(By.Id("blazor-error-ui"), TimeSpan.FromSeconds(10));
Assert.NotNull(errorUiElem);
Expand Down
1 change: 1 addition & 0 deletions src/Components/test/testassets/BasicTestApp/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<option value="BasicTestApp.HttpClientTest.HttpRequestsComponent">HttpClient tester</option>
<option value="BasicTestApp.InputEventComponent">Input events</option>
<option value="BasicTestApp.InteropComponent">Interop component</option>
<option value="BasicTestApp.InteropValueTypesComponent">Interop value types</option>
<option value="BasicTestApp.InteropOnInitializationComponent">Interop on initialization</option>
<option value="BasicTestApp.JavaScriptRootComponents">JavaScript root components</option>
<option value="BasicTestApp.JsonSerializationCases">JSON serialization</option>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<button id="btn-interop" @onclick="InvokeInteropAsync">Invoke interop!</button>

<p>
This component shows it's possible to save and load value types (in this case, a guid)
to and from localstorage,
and also that a null value can be loaded into a nullable value type, i.e. a Guid?.
</p>

<p>
String value from guid:
<strong id="string-get-by-interop">@(StringFromInterop ?? "No value yet")</strong>
</p>

<p>
Guid value from guid is default(Guid) if it wasn't loaded correctly:
<strong id="guid-get-by-interop">@GuidFromInterop</strong>
</p>

<p>
Nullable Guid value from guid is null if it wasn't loaded correctly:
<strong id="nullable-guid-get-by-interop">@NullableGuidFromInterop</strong>
</p>

<p>
Nullable load result is false if the null value wasn't loaded into the nullable guid correctly:
<strong id="null-loaded-into-nullable">@NullableLoadResult</strong>
</p>

@if (DoneWithInterop)
{
<p id="done-with-interop">
Done with interop!
</p>
}

@code {
public Guid Guid { get; } = Guid.NewGuid();
public string StringFromInterop { get; set; }
public Guid GuidFromInterop { get; set; }
public Guid? NullableGuidFromInterop { get; set; }
public bool NullableLoadResult { get; set; }
public bool DoneWithInterop { get; set; }

private async Task InvokeInteropAsync()
{
// Store guid in localStorage
await JSRuntime.InvokeVoidAsync("localStorage.setItem", "guid", Guid);

// Retrieve guid from localStorage as string because it's a simple conversion
StringFromInterop = await JSRuntime.InvokeAsync<string>("localStorage.getItem", "guid");

// Retrieve guid from localStorage as Guid
GuidFromInterop = await JSRuntime.InvokeAsync<Guid>("localStorage.getItem", "guid");

// Retrieve guid from localStorage as Guid?
NullableGuidFromInterop = await JSRuntime.InvokeAsync<Guid?>("localStorage.getItem", "guid");

// Retrieve null from localStorage as Guid?
var nullableGuid = await JSRuntime.InvokeAsync<Guid?>("localStorage.getItem", "nothingHere");

// If the retrieval crashes, the bool stays false
NullableLoadResult = !nullableGuid.HasValue;

DoneWithInterop = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ public void SetResult(object tcs, object? result)
// If necessary, attempt a cast
var typedResult = result is T resultT
? resultT
: (T)Convert.ChangeType(result, typeof(T), CultureInfo.InvariantCulture)!;
: result == null && typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>) // ChangeType can't convert null to value types
? default(T)
: (T)Convert.ChangeType(result, typeof(T), CultureInfo.InvariantCulture)!;

typedTcs.SetResult(typedResult!);
}
Expand Down
Loading