Esempio n. 1
0
        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(DotNetInvocationInfo info) => $"An error ocurred while invoking '[{info.AssemblyName}]::{info.MethodIdentifier}'. Swapping to 'Development' environment will " +
            "display more detailed information about the error that occurred.";

            var runtime = new TestJSRuntime()
            {
                OnDotNetException = (invocationInfo) => new JSError {
                    Message = GetMessage(invocationInfo)
                }
            };

            var exception  = new Exception("Some really sensitive data in here");
            var invocation = new DotNetInvocationInfo("Assembly", "Method", 0, "0");
            var result     = new DotNetInvocationResult(exception, default);

            // Act
            runtime.EndInvokeDotNet(invocation, result);

            // 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);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
            }
        }
Esempio n. 4
0
        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]);
            }
        }
Esempio n. 5
0
        public static 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);
        }
Esempio n. 6
0
        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));
            }
        }
Esempio n. 7
0
        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);
        }
Esempio n. 8
0
        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);
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        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);
        }
Esempio n. 11
0
        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);
        }
Esempio n. 12
0
        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]);
        }
Esempio n. 13
0
        public void InvokeAsync_CompletesSuccessfullyBeforeTimeout()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            runtime.DefaultTimeout = TimeSpan.FromSeconds(10);
            var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes("null"));

            // Act
            var task = runtime.InvokeAsync <object>("test identifier 1", "arg1", 123, true);

            runtime.EndInvokeJS(2, succeeded: true, ref reader);

            Assert.True(task.IsCompletedSuccessfully);
        }
Esempio n. 14
0
        public void DoubleDispose_Works()
        {
            // Arrange
            var objRef    = DotNetObjectReference.Create("Hello world");
            var jsRuntime = new TestJSRuntime();

            jsRuntime.TrackObjectReference(objRef);
            var objectId = objRef.ObjectId;

            // Act
            Assert.Same(objRef, jsRuntime.GetObjectReference(objectId));
            objRef.Dispose();

            // Assert
            objRef.Dispose();
            // If we got this far, this did not throw.
        }
Esempio n. 15
0
        public void TrackObjectReference_ThrowsIfDifferentJSRuntimeInstancesAreUsed()
        {
            // Arrange
            var objRef   = DotNetObjectReference.Create("Hello world");
            var expected = $"{objRef.GetType().Name} is already being tracked by a different instance of {nameof(JSRuntime)}. A common cause is caching an instance of {nameof(DotNetObjectReference<string>)}" +
                           $" globally. Consider creating instances of {nameof(DotNetObjectReference<string>)} at the JSInterop callsite.";
            var jsRuntime1 = new TestJSRuntime();
            var jsRuntime2 = new TestJSRuntime();

            jsRuntime1.TrackObjectReference(objRef);

            // Act
            var ex = Assert.Throws <InvalidOperationException>(() => jsRuntime2.TrackObjectReference(objRef));

            // Assert
            Assert.Equal(expected, ex.Message);
        }
Esempio n. 16
0
        public async Task InvokeAsync_DoesNotStartWorkWhenCancellationHasBeenRequested()
        {
            // Arrange
            using var cts = new CancellationTokenSource();
            cts.Cancel();
            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);

            Assert.Empty(runtime.BeginInvokeCalls);
        }
Esempio n. 17
0
        public void ReceiveByteArray_ThrowsExceptionIfUnexpectedId()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            runtime.ByteArraysToBeRevived.Append(new byte[] { 1, 5, 7 });
            runtime.ByteArraysToBeRevived.Append(new byte[] { 3, 10, 15 });

            var byteArray = new byte[] { 1, 5, 7 };

            // Act
            var ex = Assert.Throws <ArgumentOutOfRangeException>(() => runtime.ReceiveByteArray(7, byteArray));

            // Assert
            Assert.Equal(2, runtime.ByteArraysToBeRevived.Count);
            Assert.Equal("Element id '7' cannot be added to the byte arrays to be revived with length '2'.", ex.Message);
        }
Esempio n. 18
0
        public void ReceiveByteArray_ClearsByteArraysToBeRevivedWhenIdIsZero()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            runtime.ByteArraysToBeRevived.Append(new byte[] { 1, 5, 7 });
            runtime.ByteArraysToBeRevived.Append(new byte[] { 3, 10, 15 });

            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]);
        }
Esempio n. 19
0
        public void Dispose_StopsTrackingObject()
        {
            // Arrange
            var objRef    = DotNetObjectReference.Create("Hello world");
            var jsRuntime = new TestJSRuntime();

            jsRuntime.TrackObjectReference(objRef);
            var objectId = objRef.ObjectId;
            var expected = $"There is no tracked object with id '{objectId}'. Perhaps the DotNetObjectReference instance was already disposed.";

            // Act
            Assert.Same(objRef, jsRuntime.GetObjectReference(objectId));
            objRef.Dispose();

            // Assert
            Assert.True(objRef.Disposed);
            Assert.Throws <ArgumentException>(() => jsRuntime.GetObjectReference(objectId));
        }
        public void CanCompleteAsyncCallsWithComplexType()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            var task   = runtime.InvokeAsync <TestPoco>("test identifier", Array.Empty <object>());
            var bytes  = Encoding.UTF8.GetBytes("{\"id\":10, \"name\": \"Test\"}");
            var reader = new Utf8JsonReader(bytes);

            // Act/Assert: Task can be completed
            runtime.EndInvokeJS(
                runtime.BeginInvokeCalls[0].AsyncHandle,
                /* succeeded: */ true,
                ref reader);
            Assert.True(task.IsCompleted);
            var poco = task.Result;

            Assert.Equal(10, poco.Id);
            Assert.Equal("Test", poco.Name);
        }
Esempio n. 21
0
        public void CanSanitizeDotNetInteropExceptions()
        {
            // Arrange
            var runtime    = new TestJSRuntime();
            var exception  = new Exception("Some really sensitive data in here");
            var invocation = new DotNetInvocationInfo("TestAssembly", "TestMethod", 0, "0");
            var result     = new DotNetInvocationResult(exception, default);

            // Act
            runtime.EndInvokeDotNet(invocation, result);

            // Assert
            var call = runtime.EndInvokeDotNetCalls.Single();

            Assert.Equal("0", call.CallId);
            Assert.False(call.Success);

            var error = Assert.IsType <JSError>(call.ResultError);

            Assert.Same(exception, error.InnerException);
            Assert.Equal(invocation, error.InvocationInfo);
        }
Esempio n. 22
0
        public void DispatchesAsyncCallsWithDistinctAsyncHandles()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            // Act
            runtime.InvokeAsync <object>("test identifier 1", "arg1", 123, true);
            runtime.InvokeAsync <object>("test identifier 2", "some other arg");

            // Assert
            Assert.Collection(runtime.BeginInvokeCalls,
                              call =>
            {
                Assert.Equal("test identifier 1", call.Identifier);
                Assert.Equal("[\"arg1\",123,true]", call.ArgsJson);
            },
                              call =>
            {
                Assert.Equal("test identifier 2", call.Identifier);
                Assert.Equal("[\"some other arg\"]", call.ArgsJson);
                Assert.NotEqual(runtime.BeginInvokeCalls[0].AsyncHandle, call.AsyncHandle);
            });
        }
Esempio n. 23
0
        public void CanCompleteAsyncCallsAsSuccess()
        {
            // 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("\"my result\"");
            var reader = new Utf8JsonReader(bytes);

            // Act/Assert: Task can be completed
            runtime.EndInvokeJS(
                runtime.BeginInvokeCalls[1].AsyncHandle,
                /* succeeded: */ true,
                ref reader);
            Assert.False(unrelatedTask.IsCompleted);
            Assert.True(task.IsCompleted);
            Assert.Equal("my result", task.Result);
        }
Esempio n. 24
0
        public Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync()
        {
            // Arrange
            var runtime = new TestJSRuntime();

            // Act/Assert
            var task         = runtime.InvokeAsync <string>("test identifier", Array.Empty <object>());
            var asyncHandle  = runtime.BeginInvokeCalls[0].AsyncHandle;
            var firstReader  = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Some data\""));
            var secondReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Exception\""));

            runtime.EndInvokeJS(asyncHandle, true, ref firstReader);
            runtime.EndInvokeJS(asyncHandle, false, ref secondReader);

            return(AssertTask());

            async Task AssertTask()
            {
                var result = await task;

                Assert.Equal("Some data", result);
            }
        }
Esempio n. 25
0
        public void SerializesDotNetObjectWrappersInKnownFormat()
        {
            // Arrange
            var runtime = new TestJSRuntime();
            var obj1    = new object();
            var obj2    = new object();
            var obj3    = new object();

            // Act
            // Showing we can pass the DotNetObject either as top-level args or nested
            var obj1Ref          = DotNetObjectReference.Create(obj1);
            var obj1DifferentRef = DotNetObjectReference.Create(obj1);

            runtime.InvokeAsync <object>("test identifier",
                                         obj1Ref,
                                         new Dictionary <string, object>
            {
                { "obj2", DotNetObjectReference.Create(obj2) },
                { "obj3", DotNetObjectReference.Create(obj3) },
                { "obj1SameRef", obj1Ref },
                { "obj1DifferentRef", obj1DifferentRef },
            });

            // Assert: Serialized as expected
            var call = runtime.BeginInvokeCalls.Single();

            Assert.Equal("test identifier", call.Identifier);
            Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":2},\"obj3\":{\"__dotNetObject\":3},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":4}}]", call.ArgsJson);

            // Assert: Objects were tracked
            Assert.Same(obj1Ref, runtime.GetObjectReference(1));
            Assert.Same(obj1, obj1Ref.Value);
            Assert.NotSame(obj1Ref, runtime.GetObjectReference(2));
            Assert.Same(obj2, runtime.GetObjectReference(2).Value);
            Assert.Same(obj3, runtime.GetObjectReference(3).Value);
            Assert.Same(obj1, runtime.GetObjectReference(4).Value);
        }