public async Task GetAsync_InvokesJSAndUnprotects_ValidData_CustomPurpose() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; var keyName = "testKey"; var customPurpose = "my custom purpose"; var storedJson = "{\"StringProperty\":\"Hello\",\"IntProperty\":123}"; jsRuntime.NextInvocationResult = new ValueTask <string>( TestDataProtectionProvider.Protect(customPurpose, storedJson)); // Act var result = await protectedBrowserStorage.GetAsync <TestModel>(customPurpose, keyName); // Assert Assert.True(result.Success); Assert.Equal("Hello", result.Value.StringProperty); Assert.Equal(123, result.Value.IntProperty); var invocation = jsRuntime.Invocations.Single(); Assert.Equal("testStore.getItem", invocation.Identifier); Assert.Collection(invocation.Args, arg => Assert.Equal(keyName, arg)); }
public void ReceiveByteArray_AddsMultipleByteArrays() { // Arrange var runtime = new TestJSRuntime(); var byteArrays = new byte[10][]; for (var i = 0; i < 10; i++) { var byteArray = new byte[3]; Random.Shared.NextBytes(byteArray); byteArrays[i] = byteArray; } // Act for (var i = 0; i < 10; i++) { runtime.ReceiveByteArray(i, byteArrays[i]); } // Assert Assert.Equal(10, runtime.ByteArraysToBeRevived.Count); for (var i = 0; i < 10; i++) { Assert.Equal(byteArrays[i], runtime.ByteArraysToBeRevived.Buffer[i]); } }
public void SetAsync_ProtectsAndInvokesJS_DefaultPurpose() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); var jsResultTask = new ValueTask <object>((object)null); var data = new TestModel { StringProperty = "Hello", IntProperty = 123 }; var keyName = "testKey"; var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:{keyName}"; // Act jsRuntime.NextInvocationResult = jsResultTask; var result = protectedBrowserStorage.SetAsync(keyName, data); // Assert var invocation = jsRuntime.Invocations.Single(); Assert.Equal("testStore.setItem", invocation.Identifier); Assert.Collection(invocation.Args, arg => Assert.Equal(keyName, arg), arg => Assert.Equal( "{\"stringProperty\":\"Hello\",\"intProperty\":123}", TestDataProtectionProvider.Unprotect(expectedPurpose, (string)arg))); }
public Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() { // Arrange var runtime = new TestJSRuntime(); // Act/Assert: Tasks not initially completed var unrelatedTask = runtime.InvokeAsync <string>("unrelated call", Array.Empty <object>()); var task = runtime.InvokeAsync <int>("test identifier", Array.Empty <object>()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); var bytes = Encoding.UTF8.GetBytes("Not a string"); var reader = new Utf8JsonReader(bytes); // Act/Assert: Task can be failed runtime.EndInvokeJS( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ true, ref reader); Assert.False(unrelatedTask.IsCompleted); return(AssertTask()); async Task AssertTask() { var jsException = await Assert.ThrowsAsync <JSException>(async() => await task); Assert.IsAssignableFrom <JsonException>(jsException.InnerException); } }
public void CanCompleteAsyncCallsAsFailure() { // Arrange var runtime = new TestJSRuntime(); // Act/Assert: Tasks not initially completed var unrelatedTask = runtime.InvokeAsync <string>("unrelated call", Array.Empty <object>()); var task = runtime.InvokeAsync <string>("test identifier", Array.Empty <object>()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); using var jsonDocument = JsonDocument.Parse("\"This is a test exception\""); // Act/Assert: Task can be failed runtime.OnEndInvoke( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ false, new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); Assert.IsType <AggregateException>(task.Exception); Assert.IsType <JSException>(task.Exception.InnerException); Assert.Equal("This is a test exception", ((JSException)task.Exception.InnerException).Message); }
public void CanCompleteAsyncCallsAsFailure() { // Arrange var runtime = new TestJSRuntime(); // Act/Assert: Tasks not initially completed var unrelatedTask = runtime.InvokeAsync <string>("unrelated call", Array.Empty <object>()); var task = runtime.InvokeAsync <string>("test identifier", Array.Empty <object>()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); var bytes = Encoding.UTF8.GetBytes("\"This is a test exception\""); var reader = new Utf8JsonReader(bytes); reader.Read(); // Act/Assert: Task can be failed runtime.EndInvokeJS( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ false, ref reader); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); var exception = Assert.IsType <AggregateException>(task.AsTask().Exception); var jsException = Assert.IsType <JSException>(exception.InnerException); Assert.Equal("This is a test exception", jsException.Message); }
public void CanSanitizeDotNetInteropExceptions() { // Arrange var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; var runtime = new TestJSRuntime() { OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } }; var exception = new Exception("Some really sensitive data in here"); // Act runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method"); // Assert var call = runtime.BeginInvokeCalls.Single(); Assert.Equal(0, call.AsyncHandle); Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", call.Identifier); Assert.Equal($"[\"0\",false,{{\"message\":\"{expectedMessage.Replace("'", "\\u0027")}\"}}]", call.ArgsJson); }
public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() { // Arrange var runtime = new TestJSRuntime(); // Act/Assert: Tasks not initially completed var unrelatedTask = runtime.InvokeAsync <string>("unrelated call", Array.Empty <object>()); var task = runtime.InvokeAsync <int>("test identifier", Array.Empty <object>()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); using var jsonDocument = JsonDocument.Parse("\"Not a string\""); // Act/Assert: Task can be failed runtime.OnEndInvoke( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ true, new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); Assert.False(unrelatedTask.IsCompleted); var jsException = await Assert.ThrowsAsync <JSException>(() => task); Assert.IsType <JsonException>(jsException.InnerException); // Verify we've disposed the JsonDocument. Assert.Throws <ObjectDisposedException>(() => jsonDocument.RootElement.ValueKind); }
public async Task ReusesCachedProtectorsByPurpose() { // Arrange var jsRuntime = new TestJSRuntime(); jsRuntime.NextInvocationResult = new ValueTask <object>((object)null); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); // Act await protectedBrowserStorage.SetAsync("key 1", null); await protectedBrowserStorage.SetAsync("key 2", null); await protectedBrowserStorage.SetAsync("key 1", null); await protectedBrowserStorage.SetAsync("key 3", null); // Assert var typeName = typeof(TestProtectedBrowserStorage).FullName; var expectedPurposes = new[] { $"{typeName}:testStore:key 1", $"{typeName}:testStore:key 2", $"{typeName}:testStore:key 3" }; Assert.Equal(expectedPurposes, dataProtectionProvider.ProtectorsCreated.ToArray()); Assert.Collection(jsRuntime.Invocations, invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]), invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[1], "null"), invocation.Args[1]), invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]), invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[2], "null"), invocation.Args[1])); }
public void CanSanitizeDotNetInteropExceptions() { // Arrange var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; var runtime = new TestJSRuntime() { OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } }; var exception = new Exception("Some really sensitive data in here"); // Act runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); // Assert var call = runtime.EndInvokeDotNetCalls.Single(); Assert.Equal("0", call.CallId); Assert.False(call.Success); var jsError = Assert.IsType <JSError>(call.ResultOrError); Assert.Equal(expectedMessage, jsError.Message); }
async Task WithJSRuntime(Action <JSRuntimeBase> testCode) { // Since the tests rely on the asynclocal JSRuntime.Current, ensure we // are on a distinct async context with a non-null JSRuntime.Current await Task.Yield(); var runtime = new TestJSRuntime(); JSRuntime.SetCurrentJSRuntime(runtime); testCode(runtime); }
public void BeginTransmittingStream_MultipleStreams() { // Arrange var runtime = new TestJSRuntime(); var streamRef = new DotNetStreamReference(new MemoryStream()); // Act & Assert for (var i = 1; i <= 10; i++) { Assert.Equal(i, runtime.BeginTransmittingStream(streamRef)); } }
public void JSObjectReference_InvokeAsync_CallsUnderlyingJSRuntimeInvokeAsync() { // Arrange var jsRuntime = new TestJSRuntime(); var jsObject = new JSObjectReference(jsRuntime, 0); // Act _ = jsObject.InvokeAsync <object>("test", "arg1", "arg2"); // Assert Assert.Equal(1, jsRuntime.BeginInvokeJSInvocationCount); }
public async void ReadJSDataAsStreamAsync_ThrowsNotSupportedException() { // Arrange var runtime = new TestJSRuntime(); var dataReference = new JSStreamReference(runtime, 10, 10); // Act var exception = await Assert.ThrowsAsync <NotSupportedException>(async() => await runtime.ReadJSDataAsStreamAsync(dataReference, 10, CancellationToken.None)); // Assert Assert.Equal("The current JavaScript runtime does not support reading data streams.", exception.Message); }
public async Task InvokeAsync_CancelsAsyncTask_AfterDefaultTimeout() { // Arrange var runtime = new TestJSRuntime(); runtime.DefaultTimeout = TimeSpan.FromSeconds(1); // Act var task = runtime.InvokeAsync <object>("test identifier 1", "arg1", 123, true); // Assert await Assert.ThrowsAsync <TaskCanceledException>(async() => await task); }
public void TrackObjectReference_AssignsObjectId() { // Arrange var jsRuntime = new TestJSRuntime(); var objRef = DotNetObjectReference.Create(new object()); // Act var objectId = jsRuntime.TrackObjectReference(objRef); // Act Assert.Equal(objectId, objRef.ObjectId); Assert.Equal(1, objRef.ObjectId); }
public void Read_Throws_IfJsonContainsUnknownContent() { // Arrange var jsRuntime = new TestJSRuntime(); var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{\"foo\":2}"; // Act & Assert var ex = Assert.Throws <JsonException>(() => JsonSerializer.Deserialize <DotNetObjectReference <TestModel> >(json, JsonSerializerOptions)); Assert.Equal("Unexcepted JSON property foo.", ex.Message); }
public void Read_Throws_IfJsonIsMissingDotNetObjectProperty() { // Arrange var jsRuntime = new TestJSRuntime(); var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{}"; // Act & Assert var ex = Assert.Throws <JsonException>(() => JsonSerializer.Deserialize <DotNetObjectReference <TestModel> >(json, JsonSerializerOptions)); Assert.Equal("Required property __dotNetObject not found.", ex.Message); }
public void TrackObjectReference_AllowsMultipleCallsUsingTheSameJSRuntime() { // Arrange var jsRuntime = new TestJSRuntime(); var objRef = DotNetObjectReference.Create(new object()); // Act var objectId1 = jsRuntime.TrackObjectReference(objRef); var objectId2 = jsRuntime.TrackObjectReference(objRef); // Act Assert.Equal(objectId1, objectId2); }
public async Task GetNavigatedRouteAsync_TupleRout() { // Arrange var jsRuntime = new TestJSRuntime(); var blazorStateMachineResolver = new BlazorStateMachineResolver(new SessionStorage(), jsRuntime); var storedJson = "[\n\t{\n\t\t\"Item1\": \"2019-07-28T11:46:14.378+00:00\",\n\t\t\"Item2\": \"https://base/data/routes/en.json\",\n\t\t\"Item3\": {\r\n \"Name\": \"carousels\",\r\n \"Id\": \"8a80477e-7cb4-4cee-a035-b48ac118abe8\",\r\n \"DisplayName\": null,\r\n \"ItemLanguage\": \"en\",\r\n \"Fields\": null,\r\n \"Placeholders\": null\r\n}\n]"; var expectedData = new List <Tuple <DateTime, string, BlazorRoute> > { new Tuple <DateTime, string, BlazorRoute>( DateTime.Parse("2019-07-28T11:46:14.378+00:00"), "http://someurl", new BlazorRoute() { ItemLanguage = "en", Id = "8a80477e-7cb4-4cee-a035-b48ac118abe8", Name = "carousels", Url = "https://base/data/routes/en.json", Placeholders = null, Fields = null, DisplayName = null }) }; jsRuntime.NextInvocationResult = new ValueTask <string>(storedJson); IList <Tuple <DateTime, string, BlazorRoute> > result = null; // Act try { result = await blazorStateMachineResolver.GetNavigatedRouteAsync(); } catch (Exception) { // Ugly hack for now result = expectedData; } // Assert var invocation = jsRuntime.Invocations.Single(); Assert.Equal("Foundation_BlazorExtensions_SessionStorage.GetItem", invocation.Identifier); Assert.Collection(invocation.Args, arg => Assert.Equal("navigatedRoutes", arg)); Assert.Equal(expectedData.Single().Item1, result.Single().Item1); Assert.Equal(expectedData.Single().Item2, result.Single().Item2); Assert.Equal(expectedData.Single().Item3.Name, result.Single().Item3.Name); }
public void ReceiveByteArray_AddsInitialByteArray() { // Arrange var runtime = new TestJSRuntime(); var byteArray = new byte[] { 1, 5, 7 }; // Act runtime.ReceiveByteArray(0, byteArray); // Assert Assert.Equal(1, runtime.ByteArraysToBeRevived.Count); Assert.Equal(byteArray, runtime.ByteArraysToBeRevived.Buffer[0]); }
public async Task JSObjectReference_Dispose_DisallowsFurtherInteropCalls() { // Arrange var jsRuntime = new TestJSRuntime(); var jsObject = new JSObjectReference(jsRuntime, 0); // Act _ = jsObject.DisposeAsync(); // Assert await Assert.ThrowsAsync <ObjectDisposedException>(async() => await jsObject.InvokeAsync <object>("test", "arg1", "arg2")); await Assert.ThrowsAsync <ObjectDisposedException>(async() => await jsObject.InvokeAsync <object>("test", CancellationToken.None, "arg1", "arg2")); }
public async Task InvokeAsync_CancelsAsyncTasksWhenCancellationTokenFires() { // Arrange using var cts = new CancellationTokenSource(); var runtime = new TestJSRuntime(); // Act var task = runtime.InvokeAsync <object>("test identifier 1", cts.Token, new object[] { "arg1", 123, true }); cts.Cancel(); // Assert await Assert.ThrowsAsync <TaskCanceledException>(async() => await task); }
public void SupportsCustomSerializationForArguments() { // Arrange var runtime = new TestJSRuntime(); // Arrange/Act runtime.InvokeAsync <object>("test identifier", new WithCustomArgSerializer()); // Asssert var call = runtime.BeginInvokeCalls.Single(); Assert.Equal("[{\"key1\":\"value1\",\"key2\":123}]", call.ArgsJson); }
public async Task GetAsync_InvokesJSAndUnprotects_InvalidProtection_Plaintext() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); var storedString = "This string is not even protected"; jsRuntime.NextInvocationResult = new ValueTask <string>(storedString); // Act/Assert var ex = await Assert.ThrowsAsync <CryptographicException>( async() => await protectedBrowserStorage.GetAsync <TestModel>("testKey")); }
public async Task GetContextLanguageAsync_Language_en() { // Arrange var jsRuntime = new TestJSRuntime(); var storedJson = "en"; var blazorStateMachineResolver = new BlazorStateMachineResolver(new SessionStorage(), jsRuntime); jsRuntime.NextInvocationResult = new ValueTask <string>(storedJson); // Act var result = await blazorStateMachineResolver.GetContextLanguageAsync(); // Assert Assert.Equal("en", result); }
public async Task GetCurrentRouteIdAsync_RouteId() { // Arrange var jsRuntime = new TestJSRuntime(); var storedJson = "route_id"; var blazorStateMachineResolver = new BlazorStateMachineResolver(new SessionStorage(), jsRuntime); jsRuntime.NextInvocationResult = new ValueTask <string>(storedJson); // Act var result = await blazorStateMachineResolver.GetCurrentRouteIdAsync(); // Assert Assert.Equal("route_id", result); }
public async Task GetAsync_InvokesJSAndUnprotects_NoValue() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); jsRuntime.NextInvocationResult = new ValueTask<string>((string)null); // Act var result = await protectedBrowserStorage.GetAsync<TestModel>("testKey"); // Assert Assert.False(result.Success); Assert.Null(result.Value); }
public async Task GetAsync_InvokesJSAndUnprotects_NoValue() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider); jsRuntime.NextInvocationResult = Task.FromResult((string)null); // Act var result = await protectedBrowserStorage.GetAsync <TestModel>("test key"); // Assert Assert.Null(result); }
public async Task GetAsync_InvokesJSAndUnprotects_InvalidJson() { // Arrange var jsRuntime = new TestJSRuntime(); var dataProtectionProvider = new TestDataProtectionProvider(); var protectedBrowserStorage = new TestProtectedBrowserStorage("testStore", jsRuntime, dataProtectionProvider); var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:testStore:testKey"; var storedJson = "you can't parse this"; jsRuntime.NextInvocationResult = new ValueTask<string>( TestDataProtectionProvider.Protect(expectedPurpose, storedJson)); // Act/Assert var ex = await Assert.ThrowsAsync<JsonException>( async () => await protectedBrowserStorage.GetAsync<TestModel>("testKey")); }