diff --git a/Jint.Tests.PublicInterface/ShadowRealmTests.cs b/Jint.Tests.PublicInterface/ShadowRealmTests.cs index 68cf239d3..025fda993 100644 --- a/Jint.Tests.PublicInterface/ShadowRealmTests.cs +++ b/Jint.Tests.PublicInterface/ShadowRealmTests.cs @@ -23,7 +23,7 @@ public void CanUseViaEngineMethods() // modules var importValue = shadowRealm.ImportValue("./modules/format-name.js", "formatName"); - var formatName = (ObjectInstance) importValue.UnwrapIfPromise(); + var formatName = (ObjectInstance) importValue.UnwrapIfPromise(TestContext.Current.CancellationToken); var result = engine.Invoke(formatName, "John", "Doe").AsString(); Assert.Equal("John Doe", result); } diff --git a/Jint.Tests/Runtime/AsyncTests.cs b/Jint.Tests/Runtime/AsyncTests.cs index d63c605b2..41b050264 100644 --- a/Jint.Tests/Runtime/AsyncTests.cs +++ b/Jint.Tests/Runtime/AsyncTests.cs @@ -11,7 +11,7 @@ public void AwaitPropagationAgainstPrimitiveValue() { var engine = new Engine(); var result = engine.Evaluate("(async ()=>await '1')()"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("1", result); } @@ -21,7 +21,7 @@ public void ShouldTaskConvertedToPromiseInJS() Engine engine = new(); engine.SetValue("callable", Callable); var result = engine.Evaluate("callable().then(x=>x*2)"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(2, result); static async Task Callable() @@ -38,7 +38,7 @@ public void ShouldReturnedTaskConvertedToPromiseInJS() Engine engine = new(options => options.ExperimentalFeatures = ExperimentalFeature.TaskInterop); engine.SetValue("asyncTestClass", new AsyncTestClass()); var result = engine.Evaluate("asyncTestClass.ReturnDelayedTaskAsync().then(x=>x)"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(AsyncTestClass.TestString, result); } @@ -48,7 +48,7 @@ public void ShouldUnwrapPromiseWithCustomTimeout() Engine engine = new(options => options.ExperimentalFeatures = ExperimentalFeature.TaskInterop); engine.SetValue("asyncTestClass", new AsyncTestClass()); var result = engine.Evaluate("asyncTestClass.ReturnDelayedTaskAsync().then(x=>x)"); - result = result.UnwrapIfPromise(TimeSpan.FromMilliseconds(200)); + result = result.UnwrapIfPromise(TimeSpan.FromMilliseconds(200), TestContext.Current.CancellationToken); Assert.Equal(AsyncTestClass.TestString, result); } @@ -62,7 +62,7 @@ async function test() { return await asyncTestClass.ReturnDelayedTaskAsync(); } """); - var result = engine.Invoke("test").UnwrapIfPromise(); + var result = engine.Invoke("test").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(AsyncTestClass.TestString, result); } @@ -72,7 +72,7 @@ public void ShouldReturnedCompletedTaskConvertedToPromiseInJS() Engine engine = new(options => options.ExperimentalFeatures = ExperimentalFeature.TaskInterop); engine.SetValue("asyncTestClass", new AsyncTestClass()); var result = engine.Evaluate("asyncTestClass.ReturnCompletedTask().then(x=>x)"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(AsyncTestClass.TestString, result); } @@ -86,7 +86,7 @@ public void ShouldTaskCatchWhenCancelled() engine.SetValue("callable", Callable); engine.SetValue("assert", new Action(Assert.True)); var result = engine.Evaluate("callable(token).then(_ => assert(false)).catch(_ => assert(true))"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); static async Task Callable(CancellationToken token) { await Task.FromCanceled(token); @@ -103,7 +103,7 @@ public void ShouldReturnedTaskCatchWhenCancelled() engine.SetValue("asyncTestClass", new AsyncTestClass()); engine.SetValue("assert", new Action(Assert.True)); var result = engine.Evaluate("asyncTestClass.ReturnCancelledTask(token).then(_ => assert(false)).catch(_ => assert(true))"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); } [Fact] @@ -128,7 +128,7 @@ public void ShouldReturnedTaskCatchWhenThrowError() engine.SetValue("asyncTestClass", new AsyncTestClass()); engine.SetValue("assert", new Action(Assert.True)); var result = engine.Evaluate("asyncTestClass.ThrowAfterDelayAsync().then(_ => assert(false)).catch(_ => assert(true))"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); } [Fact] @@ -160,7 +160,7 @@ public void ShouldValueTaskConvertedToPromiseInJS() Engine engine = new(); engine.SetValue("callable", Callable); var result = engine.Evaluate("callable().then(x=>x*2)"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(2, result); static async ValueTask Callable() @@ -181,7 +181,7 @@ public void ShouldValueTaskCatchWhenCancelled() engine.SetValue("callable", Callable); engine.SetValue("assert", new Action(Assert.True)); var result = engine.Evaluate("callable(token).then(_ => assert(false)).catch(_ => assert(true))"); - result = result.UnwrapIfPromise(); + result = result.UnwrapIfPromise(TestContext.Current.CancellationToken); static async ValueTask Callable(CancellationToken token) { await ValueTask.FromCanceled(token); @@ -277,7 +277,7 @@ async function main() { """; var result = engine.Execute(Script); var val = result.GetValue("main"); - val.Call().UnwrapIfPromise(); + val.Call().UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(2, log.Count); Assert.Equal("Promise!", log[0]); Assert.Equal("Resolved!", log[1]); @@ -302,7 +302,7 @@ async function main() { """; var result = engine.Execute(Script); var val = result.GetValue("main").Call(); - Assert.Equal(1, val.UnwrapIfPromise().AsInteger()); + Assert.Equal(1, val.UnwrapIfPromise(TestContext.Current.CancellationToken).AsInteger()); } [Fact] diff --git a/Jint.Tests/Runtime/InteropDisposeTests.cs b/Jint.Tests/Runtime/InteropDisposeTests.cs index 76aeb1ddb..7fdc700e9 100644 --- a/Jint.Tests/Runtime/InteropDisposeTests.cs +++ b/Jint.Tests/Runtime/InteropDisposeTests.cs @@ -46,7 +46,7 @@ public void Dispose() [InlineData("(async function x() { await using temp = getAsync(); })();")] public void ShouldAsyncDispose(string program) { - _engine.Evaluate(program).UnwrapIfPromise(); + _engine.Evaluate(program).UnwrapIfPromise(TestContext.Current.CancellationToken); _asyncDisposable.Disposed.Should().BeTrue(); } diff --git a/Jint.Tests/Runtime/PromiseTests.cs b/Jint.Tests/Runtime/PromiseTests.cs index 91cd477ce..41e4ece28 100644 --- a/Jint.Tests/Runtime/PromiseTests.cs +++ b/Jint.Tests/Runtime/PromiseTests.cs @@ -25,7 +25,7 @@ public void RegisterPromise_CalledWithinExecute_ResolvesCorrectly() var promise = engine.Evaluate("f();"); resolveFunc(66); - Assert.Equal(66, promise.UnwrapIfPromise()); + Assert.Equal(66, promise.UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -47,7 +47,7 @@ public void RegisterPromise_CalledWithinExecute_RejectsCorrectly() rejectFunc("oops!"); - var ex = Assert.Throws(() => { completion.UnwrapIfPromise(); }); + var ex = Assert.Throws(() => { completion.UnwrapIfPromise(TestContext.Current.CancellationToken); }); Assert.Equal("oops!", ex.RejectedValue.AsString()); } @@ -78,12 +78,12 @@ public void RegisterPromise_UsedWithRace_WorksFlawlessly() resolve1("first"); // still not finished but the promise is fulfilled - Assert.Equal("first", completion.UnwrapIfPromise()); + Assert.Equal("first", completion.UnwrapIfPromise(TestContext.Current.CancellationToken)); resolve2("second"); // completion value hasn't changed - Assert.Equal("first", completion.UnwrapIfPromise()); + Assert.Equal("first", completion.UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -134,7 +134,7 @@ public void PromiseCtor_ReturnsPromiseJsValue() public void PromiseResolveViaResolver_ReturnsCorrectValue() { var engine = new Engine(); - var res = engine.Evaluate("new Promise((resolve, reject)=>{resolve(66);});").UnwrapIfPromise(); + var res = engine.Evaluate("new Promise((resolve, reject)=>{resolve(66);});").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(66, res); } @@ -142,7 +142,7 @@ public void PromiseResolveViaResolver_ReturnsCorrectValue() public void PromiseResolveViaStatic_ReturnsCorrectValue() { var engine = new Engine(); - Assert.Equal(66, engine.Evaluate("Promise.resolve(66);").UnwrapIfPromise()); + Assert.Equal(66, engine.Evaluate("Promise.resolve(66);").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -152,7 +152,7 @@ public void PromiseRejectViaResolver_ThrowsPromiseRejectedException() var ex = Assert.Throws(() => { - engine.Evaluate("new Promise((resolve, reject)=>{reject('Could not connect');});").UnwrapIfPromise(); + engine.Evaluate("new Promise((resolve, reject)=>{reject('Could not connect');});").UnwrapIfPromise(TestContext.Current.CancellationToken); }); Assert.Equal("Could not connect", ex.RejectedValue.AsString()); @@ -165,7 +165,7 @@ public void PromiseRejectViaStatic_ThrowsPromiseRejectedException() var ex = Assert.Throws(() => { - engine.Evaluate("Promise.reject('Could not connect');").UnwrapIfPromise(); + engine.Evaluate("Promise.reject('Could not connect');").UnwrapIfPromise(TestContext.Current.CancellationToken); }); Assert.Equal("Could not connect", ex.RejectedValue.AsString()); @@ -177,7 +177,7 @@ public void PromiseChainedThen_HandlerCalledWithCorrectValue() var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => 44).then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => 44).then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(44, res); } @@ -187,7 +187,7 @@ public void PromiseThen_ReturnsNewPromiseInstance() { var engine = new Engine(); var res = engine.Evaluate( - "var promise1 = new Promise((resolve, reject) => { resolve(1); }); var promise2 = promise1.then(); promise1 === promise2").UnwrapIfPromise(); + "var promise1 = new Promise((resolve, reject) => { resolve(1); }); var promise2 = promise1.then(); promise1 === promise2").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(false, res); } @@ -197,7 +197,7 @@ public void PromiseThen_CalledCorrectlyOnResolve() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(66, res); } @@ -207,7 +207,7 @@ public void PromiseResolveChainedWithHandler_ResolvedAsUndefined() { var engine = new Engine(); - Assert.Equal(JsValue.Undefined, engine.Evaluate("Promise.resolve(33).then(() => {});").UnwrapIfPromise()); + Assert.Equal(JsValue.Undefined, engine.Evaluate("Promise.resolve(33).then(() => {});").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -215,7 +215,7 @@ public void PromiseChainedThenWithUndefinedCallback_PassesThroughValueCorrectly( { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then().then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then().then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(66, res); } @@ -225,7 +225,7 @@ public void PromiseChainedThenWithCallbackReturningUndefined_PassesThroughUndefi { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => {}).then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => {}).then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(JsValue.Undefined, res); } @@ -235,7 +235,7 @@ public void PromiseChainedThenThrowsError_ChainedCallsCatchWithThrownError() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => { throw 'Thrown Error'; }).catch(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => { throw 'Thrown Error'; }).catch(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Thrown Error", res); } @@ -245,7 +245,7 @@ public void PromiseChainedThenReturnsResolvedPromise_ChainedCallsThenWithPromise { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => Promise.resolve(55)).then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => Promise.resolve(55)).then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(55, res); } @@ -255,7 +255,7 @@ public void PromiseChainedThenReturnsRejectedPromise_ChainedCallsCatchWithPromis { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => Promise.reject('Error Message')).catch(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).then(() => Promise.reject('Error Message')).catch(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Error Message", res); } @@ -265,7 +265,7 @@ public void PromiseCatch_CalledCorrectlyOnReject() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Could not connect", res); } @@ -275,7 +275,7 @@ public void PromiseThenWithCatch_CalledCorrectlyOnReject() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).then(undefined, result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).then(undefined, result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Could not connect", res); } @@ -284,7 +284,7 @@ public void PromiseThenWithCatch_CalledCorrectlyOnReject() public void PromiseChainedWithHandler_ResolvedAsUndefined() { var engine = new Engine(); - Assert.Equal(JsValue.Undefined, engine.Evaluate("Promise.reject('error').catch(() => {});").UnwrapIfPromise()); + Assert.Equal(JsValue.Undefined, engine.Evaluate("Promise.reject('error').catch(() => {});").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -292,7 +292,7 @@ public void PromiseChainedCatchThen_ThenCallWithUndefined() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch(ex => {}).then(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch(ex => {}).then(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(JsValue.Undefined, res); } @@ -302,7 +302,7 @@ public void PromiseChainedCatchWithUndefinedHandler_CatchChainedCorrectly() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch().catch(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerReject('Could not connect')}).catch().catch(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Could not connect", res); } @@ -312,7 +312,7 @@ public void PromiseChainedFinally_HandlerCalled() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).finally(() => resolve(16)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(66)}).finally(() => resolve(16)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(16, res); } @@ -331,14 +331,14 @@ public void PromiseFinally_ReturnsNewPromiseInstance() public void PromiseFinally_ResolvesWithCorrectValue() { var engine = new Engine(); - Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally(() => {})").UnwrapIfPromise()); + Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally(() => {})").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] public void PromiseFinallyWithNoCallback_ResolvesWithCorrectValue() { var engine = new Engine(); - Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally()").UnwrapIfPromise()); + Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally()").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -346,7 +346,7 @@ public void PromiseFinallyChained_ResolvesWithCorrectValue() { var engine = new Engine(); - Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally(() => 6).finally(() => 9);").UnwrapIfPromise()); + Assert.Equal(2, engine.Evaluate("Promise.resolve(2).finally(() => 6).finally(() => 9);").UnwrapIfPromise(TestContext.Current.CancellationToken)); } [Fact] @@ -354,7 +354,7 @@ public void PromiseFinallyWhichThrows_ResolvesWithError() { var engine = new Engine(); var res = engine.Evaluate( - "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(5)}).finally(() => {throw 'Could not connect';}).catch(result => resolve(result)); });").UnwrapIfPromise(); + "new Promise((resolve, reject) => { new Promise((innerResolve, innerReject) => {innerResolve(5)}).finally(() => {throw 'Could not connect';}).catch(result => resolve(result)); });").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal("Could not connect", res); } @@ -363,7 +363,7 @@ public void PromiseFinallyWhichThrows_ResolvesWithError() public void PromiseAll_BadIterable_Rejects() { var engine = new Engine(); - Assert.Throws(() => { engine.Evaluate("Promise.all();").UnwrapIfPromise(); }); + Assert.Throws(() => { engine.Evaluate("Promise.all();").UnwrapIfPromise(TestContext.Current.CancellationToken); }); } @@ -372,7 +372,7 @@ public void PromiseAll_ArgsAreNotPromises_ResolvesCorrectly() { var engine = new Engine(); - Assert.Equal(new object[] {1d, 2d, 3d}, engine.Evaluate("Promise.all([1,2,3]);").UnwrapIfPromise().ToObject()); + Assert.Equal(new object[] {1d, 2d, 3d}, engine.Evaluate("Promise.all([1,2,3]);").UnwrapIfPromise(TestContext.Current.CancellationToken).ToObject()); } [Fact] @@ -380,7 +380,7 @@ public void PromiseAll_MixturePromisesNoPromises_ResolvesCorrectly() { var engine = new Engine(); Assert.Equal(new object[] {1d, 2d, 3d}, - engine.Evaluate("Promise.all([1,Promise.resolve(2),3]);").UnwrapIfPromise().ToObject()); + engine.Evaluate("Promise.all([1,Promise.resolve(2),3]);").UnwrapIfPromise(TestContext.Current.CancellationToken).ToObject()); } [Fact] @@ -390,7 +390,7 @@ public void PromiseAll_MixturePromisesNoPromisesOneRejects_ResolvesCorrectly() Assert.Throws(() => { - engine.Evaluate("Promise.all([1,Promise.resolve(2),3, Promise.reject('Cannot connect')]);").UnwrapIfPromise(); + engine.Evaluate("Promise.all([1,Promise.resolve(2),3, Promise.reject('Cannot connect')]);").UnwrapIfPromise(TestContext.Current.CancellationToken); }); } @@ -399,7 +399,7 @@ public void PromiseRace_NoArgs_Rejects() { var engine = new Engine(); - Assert.Throws(() => { engine.Evaluate("Promise.race();").UnwrapIfPromise(); }); + Assert.Throws(() => { engine.Evaluate("Promise.race();").UnwrapIfPromise(TestContext.Current.CancellationToken); }); } [Fact] @@ -407,7 +407,7 @@ public void PromiseRace_InvalidIterator_Rejects() { var engine = new Engine(); - Assert.Throws(() => { engine.Evaluate("Promise.race({});").UnwrapIfPromise(); }); + Assert.Throws(() => { engine.Evaluate("Promise.race({});").UnwrapIfPromise(TestContext.Current.CancellationToken); }); } [Fact] @@ -415,7 +415,7 @@ public void PromiseRaceNoPromises_ResolvesCorrectly() { var engine = new Engine(); - Assert.Equal(12d, engine.Evaluate("Promise.race([12,2,3]);").UnwrapIfPromise().ToObject()); + Assert.Equal(12d, engine.Evaluate("Promise.race([12,2,3]);").UnwrapIfPromise(TestContext.Current.CancellationToken).ToObject()); } [Fact] @@ -423,7 +423,7 @@ public void PromiseRaceMixturePromisesNoPromises_ResolvesCorrectly() { var engine = new Engine(); - Assert.Equal(12d, engine.Evaluate("Promise.race([12,Promise.resolve(2),3]);").UnwrapIfPromise().ToObject()); + Assert.Equal(12d, engine.Evaluate("Promise.race([12,Promise.resolve(2),3]);").UnwrapIfPromise(TestContext.Current.CancellationToken).ToObject()); } [Fact] @@ -431,14 +431,14 @@ public void PromiseRaceMixturePromisesNoPromises_ResolvesCorrectly2() { var engine = new Engine(); - Assert.Equal(2d, engine.Evaluate("Promise.race([Promise.resolve(2),6,3]);").UnwrapIfPromise().ToObject()); + Assert.Equal(2d, engine.Evaluate("Promise.race([Promise.resolve(2),6,3]);").UnwrapIfPromise(TestContext.Current.CancellationToken).ToObject()); } [Fact] public void PromiseRaceMixturePromisesNoPromises_ResolvesCorrectly3() { var engine = new Engine(); - var res = engine.Evaluate("Promise.race([new Promise((resolve,reject)=>{}),Promise.resolve(55),3]);").UnwrapIfPromise(); + var res = engine.Evaluate("Promise.race([new Promise((resolve,reject)=>{}),Promise.resolve(55),3]);").UnwrapIfPromise(TestContext.Current.CancellationToken); Assert.Equal(55d, res.ToObject()); } @@ -451,7 +451,7 @@ public void PromiseRaceMixturePromisesNoPromises_ResolvesCorrectly4() Assert.Throws(() => { engine.Evaluate( - "Promise.race([new Promise((resolve,reject)=>{}),Promise.reject('Could not connect'),3]);").UnwrapIfPromise(); + "Promise.race([new Promise((resolve,reject)=>{}),Promise.reject('Could not connect'),3]);").UnwrapIfPromise(TestContext.Current.CancellationToken); }); } @@ -470,7 +470,7 @@ public void PromiseRegression_SingleElementArrayWithClrDictionaryInPromiseAll() .Evaluate(@" const promiseArray = [clrDictionary]; return Promise.all(promiseArray);") // Returning and array through Promise.any() - .UnwrapIfPromise() + .UnwrapIfPromise(TestContext.Current.CancellationToken) .ToObject(); var result = (object[]) resultAsObject; diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs index 759aa4ab9..42b965f46 100644 --- a/Jint/JsValueExtensions.cs +++ b/Jint/JsValueExtensions.cs @@ -1,6 +1,7 @@ using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading; using Jint.Native; using Jint.Native.Function; using Jint.Native.Object; @@ -673,25 +674,97 @@ public static JsValue UnwrapIfPromise(this JsValue value, TimeSpan timeout) Throw.PromiseRejectedException($"Timeout of {timeout} reached"); } - switch (promise.State) + return HandlePromiseResult(promise); + } + + return value; + } + + /// + /// If the value is a Promise + /// 1. If "Fulfilled" returns the value it was fulfilled with + /// 2. If "Rejected" throws "PromiseRejectedException" with the rejection reason + /// 3. If "Pending" throws "InvalidOperationException". Should be called only in "Settled" state + /// Else + /// returns the value intact + /// + /// value to unwrap + /// cancellationToken to observe + /// inner value if Promise the value itself otherwise + public static JsValue UnwrapIfPromise(this JsValue value, CancellationToken cancellationToken) + { + if (value is JsPromise promise) + { + var engine = promise.Engine; + var completedEvent = promise.CompletedEvent; + + engine.RunAvailableContinuations(); + completedEvent.Wait(cancellationToken); + if (cancellationToken.IsCancellationRequested) + { + Throw.PromiseRejectedException("Operation was cancelled"); + } + + return HandlePromiseResult(promise); + } + + return value; + } + + /// + /// If the value is a Promise + /// 1. If "Fulfilled" returns the value it was fulfilled with + /// 2. If "Rejected" throws "PromiseRejectedException" with the rejection reason + /// 3. If "Pending" throws "InvalidOperationException". Should be called only in "Settled" state + /// Else + /// returns the value intact + /// + /// value to unwrap + /// timeout to wait + /// cancellationToken to observe + /// inner value if Promise the value itself otherwise + public static JsValue UnwrapIfPromise(this JsValue value, TimeSpan timeout, CancellationToken cancellationToken) + { + if (value is JsPromise promise) + { + var engine = promise.Engine; + var completedEvent = promise.CompletedEvent; + + engine.RunAvailableContinuations(); + if (!completedEvent.Wait(timeout, cancellationToken)) { - case PromiseState.Pending: - Throw.InvalidOperationException("'UnwrapIfPromise' called before Promise was settled"); - return null; - case PromiseState.Fulfilled: - return promise.Value; - case PromiseState.Rejected: - Throw.PromiseRejectedException(promise.Value); - return null; - default: - Throw.ArgumentOutOfRangeException(); - return null; + if (cancellationToken.IsCancellationRequested) + { + Throw.PromiseRejectedException("Operation was cancelled"); + } + + Throw.PromiseRejectedException($"Timeout of {timeout} reached"); } + + return HandlePromiseResult(promise); } return value; } + private static JsValue HandlePromiseResult(JsPromise promise) + { + switch (promise.State) + { + case PromiseState.Pending: + Throw.InvalidOperationException("'UnwrapIfPromise' called before Promise was settled"); + return null; + case PromiseState.Fulfilled: + return promise.Value; + case PromiseState.Rejected: + Throw.PromiseRejectedException(promise.Value); + return null; + default: + Throw.ArgumentOutOfRangeException(); + return null; + } + } + [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowWrongTypeException(JsValue value, string expectedType) {