Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion AspNetCore.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<Project Path="eng/tools/BaselineGenerator/BaselineGenerator.csproj" />
<Project Path="eng/tools/RepoTasks/RepoTasks.csproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path="src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts" />
</Folder>
<Folder Name="/src/" />
<Folder Name="/src/Analyzers/" />
<Folder Name="/src/Analyzers/Analyzers/">
Expand Down Expand Up @@ -1200,8 +1203,8 @@
<Project Path="src/Validation/src/Microsoft.Extensions.Validation.csproj" />
</Folder>
<Folder Name="/src/Validation/test/">
<Project Path="src/Validation/test/Microsoft.Extensions.Validation.Tests/Microsoft.Extensions.Validation.Tests.csproj" />
<Project Path="src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/Microsoft.Extensions.Validation.GeneratorTests.csproj" />
<Project Path="src/Validation/test/Microsoft.Extensions.Validation.Tests/Microsoft.Extensions.Validation.Tests.csproj" />
</Folder>
<Folder Name="/src/WebEncoders/">
<Project Path="src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj" />
Expand Down
10 changes: 6 additions & 4 deletions src/Components/test/E2ETest/Tests/InteropTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ public void CanInvokeInteropMethods()
["invokeVoidAsyncReturnsWithoutSerializing"] = "Success",
["invokeVoidAsyncReturnsWithoutSerializingInJSObjectReference"] = "Success",
["invokeAsyncThrowsSerializingCircularStructure"] = "Success",
["invokeAsyncThrowsUndefinedJSObjectReference"] = "Success",
["invokeAsyncThrowsNullJSObjectReference"] = "Success",
["invokeAsyncUndefinedJSObjectReference"] = "Success",
["invokeAsyncNullJSObjectReference"] = "Success",
["invokeAsyncNullFromVariableJSObjectReference"] = "Success",
["disposeJSObjectReferenceAsync"] = "Success",
// GetValue tests
["getValueFromDataPropertyAsync"] = "10",
Expand Down Expand Up @@ -148,8 +149,9 @@ public void CanInvokeInteropMethods()
["invokeVoidReturnsWithoutSerializingIJSInProcessRuntime"] = "Success",
["invokeVoidReturnsWithoutSerializingInIJSInProcessObjectReference"] = "Success",
["invokeThrowsSerializingCircularStructure"] = "Success",
["invokeThrowsUndefinedJSObjectReference"] = "Success",
["invokeThrowsNullJSObjectReference"] = "Success",
["invokeUndefinedJSObjectReference"] = "Success",
["invokeNullJSObjectReference"] = "Success",
["invokeNullFromVariableJSObjectReference"] = "Success",
["stringValueUpperSync"] = "MY STRING",
["testDtoNonSerializedValueSync"] = "99999",
["testDtoSync"] = "Same",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,29 +170,43 @@
try
{
var undefinedJsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnUndefined");
ReturnValues["invokeAsyncThrowsUndefinedJSObjectReference"] = undefinedJsObjectReference is null ? "Failure: null" : "Failure: not null";
ReturnValues["invokeAsyncUndefinedJSObjectReference"] = undefinedJsObjectReference is null ? "Success" : "Failure: not null";
}
catch (JSException)
catch (JSException ex)
{
ReturnValues["invokeAsyncThrowsUndefinedJSObjectReference"] = "Success";
ReturnValues["invokeAsyncUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
}
catch (Exception ex)
{
ReturnValues["invokeAsyncThrowsUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
ReturnValues["invokeAsyncUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
}

try
{
var nullJsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnNull");
ReturnValues["invokeAsyncThrowsNullJSObjectReference"] = nullJsObjectReference is null ? "Failure: null" : "Failure: not null";
ReturnValues["invokeAsyncNullJSObjectReference"] = nullJsObjectReference is null ? "Success" : "Failure: not null";
}
catch (JSException ex)
{
ReturnValues["invokeAsyncNullJSObjectReference"] = $"Failure: {ex.Message}";
}
catch (Exception ex)
{
ReturnValues["invokeAsyncNullJSObjectReference"] = $"Failure: {ex.Message}";
}

try
{
var nullVariableJsObjectReference = await JSRuntime.GetValueAsync<IJSObjectReference>("jsInteropTests.testObject.nullProperty");
ReturnValues["invokeAsyncNullFromVariableJSObjectReference"] = nullVariableJsObjectReference is null ? "Success" : "Failure: not null";
}
catch (JSException)
{
ReturnValues["invokeAsyncThrowsNullJSObjectReference"] = "Success";
ReturnValues["invokeAsyncNullFromVariableJSObjectReference"] = "Failure";
}
catch (Exception ex)
{
ReturnValues["invokeAsyncThrowsNullJSObjectReference"] = $"Failure: {ex.Message}";
ReturnValues["invokeAsyncNullFromVariableJSObjectReference"] = $"Failure: {ex.Message}";
}

var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
Expand Down Expand Up @@ -397,29 +411,46 @@
try
{
var undefinedJsObjectReference = inProcRuntime.Invoke<IJSObjectReference>("returnUndefined");
ReturnValues["invokeThrowsUndefinedJSObjectReference"] = undefinedJsObjectReference is null ? "Failure: null" : "Failure: not null";
ReturnValues["invokeUndefinedJSObjectReference"] = undefinedJsObjectReference is null ? "Success" : "Failure: not null";
Console.WriteLine($"Received undefined JSObjectReference: {undefinedJsObjectReference}");
}
catch (JSException)
catch (JSException ex)
{
ReturnValues["invokeThrowsUndefinedJSObjectReference"] = "Success";
ReturnValues["invokeUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
}
catch (Exception ex)
{
ReturnValues["invokeThrowsUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
ReturnValues["invokeUndefinedJSObjectReference"] = $"Failure: {ex.Message}";
}

try
{
var nullJsObjectReference = inProcRuntime.Invoke<IJSObjectReference>("returnNull");
ReturnValues["invokeThrowsNullJSObjectReference"] = nullJsObjectReference is null ? "Failure: null" : "Failure: not null";
ReturnValues["invokeNullJSObjectReference"] = nullJsObjectReference is null ? "Success" : "Failure: not null";
Console.WriteLine($"Received undefined JSObjectReference: {nullJsObjectReference}");
}
catch (JSException)
catch (JSException ex)
{
ReturnValues["invokeNullJSObjectReference"] = $"Failure: {ex.Message}";
}
catch (Exception ex)
{
ReturnValues["invokeNullJSObjectReference"] = $"Failure: {ex.Message}";
}

try
{
var nullVariableJsObjectReference = inProcRuntime.GetValue<IJSObjectReference>("jsInteropTests.testObject.nullProperty");
ReturnValues["invokeNullFromVariableJSObjectReference"] = nullVariableJsObjectReference is null ? "Success" : "Failure: not null";
Console.WriteLine($"Received undefined JSObjectReference: {nullVariableJsObjectReference}");
}
catch (JSException ex)
{
ReturnValues["invokeThrowsNullJSObjectReference"] = "Success";
ReturnValues["invokeNullFromVariableJSObjectReference"] = $"Failure: {ex.Message}";
}
catch (Exception ex)
{
ReturnValues["invokeThrowsNullJSObjectReference"] = $"Failure: {ex.Message}";
ReturnValues["invokeNullFromVariableJSObjectReference"] = $"Failure: {ex.Message}";
}

var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ const testObject = {
},
set setOnlyProperty(value) {
this.num = value;
}
},
nullProperty: null
}

window.jsInteropTests = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ export module DotNet {
* @throws Error if the given value is not an Object.
*/
export function createJSObjectReference(jsObject: any): any {
if (jsObject === null || jsObject === undefined) {
return {
[jsObjectIdKey]: -1
};
}

if (jsObject && (typeof jsObject === "object" || jsObject instanceof Function)) {
cachedJSObjectsById[nextJsObjectId] = new JSObject(jsObject);

Expand Down Expand Up @@ -220,7 +226,7 @@ export module DotNet {
export function disposeJSObjectReference(jsObjectReference: any): void {
const id = jsObjectReference && jsObjectReference[jsObjectIdKey];

if (typeof id === "number") {
if (typeof id === "number" && id !== -1) {
disposeJSObjectReferenceById(id);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,55 @@ describe("CallDispatcher", () => {

expect(result2).toBe("30");
});

test("createJSObjectReference: Handles null values without throwing", () => {
const nullRef = DotNet.createJSObjectReference(null);
expect(nullRef).toEqual({ [jsObjectId]: -1 });
});

test("createJSObjectReference: Handles undefined values without throwing", () => {
const undefinedRef = DotNet.createJSObjectReference(undefined);
expect(undefinedRef).toEqual({ [jsObjectId]: -1 });
});

test("disposeJSObjectReference: Safely handles null reference disposal", () => {
const nullRef = DotNet.createJSObjectReference(null);
expect(() => DotNet.disposeJSObjectReference(nullRef)).not.toThrow();
});

test("createJSObjectReference: Still throws for invalid types", () => {
expect(() => DotNet.createJSObjectReference("string")).toThrow();
expect(() => DotNet.createJSObjectReference(123)).toThrow();
expect(() => DotNet.createJSObjectReference(true)).toThrow();
});

test("GetValue: Returns JSObjectReference with sentinel value for null property", () => {
const testObject = { nullProp: null };
const objectId = getObjectReferenceId(testObject);

const result = dispatcher.invokeJSFromDotNet(
"nullProp",
"[]",
DotNet.JSCallResultType.JSObjectReference,
objectId,
DotNet.JSCallType.GetValue
);

expect(result).toBe('{"__jsObjectId":-1}');
});

test("GetValue: Returns JSObjectReference with sentinel value for undefined property", () => {
const testObject = { undefinedProp: undefined };
const objectId = getObjectReferenceId(testObject);

const result = dispatcher.invokeJSFromDotNet(
"undefinedProp",
"[]",
DotNet.JSCallResultType.JSObjectReference,
objectId,
DotNet.JSCallType.GetValue
);

expect(result).toBe('{"__jsObjectId":-1}');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public override bool CanConvert(Type typeToConvert)
public override IJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var id = JSObjectReferenceJsonWorker.ReadJSObjectReferenceIdentifier(ref reader);

if (id == -1)
{
return null;
}

return new JSObjectReference(_jsRuntime, id);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,17 @@ public void Write_WritesValidJson()
// Assert
Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);
}

[Fact]
public void Read_ReturnsNull_WhenJSObjectIdIsMinusOne()
{
// Arrange
var json = "{\"__jsObjectId\":-1}";

// Act
var deserialized = JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions);

// Assert
Assert.Null(deserialized);
}
}
Loading