From: <rb...@us...> - 2017-10-08 13:07:31
|
Revision: 14865 http://sourceforge.net/p/htmlunit/code/14865 Author: rbri Date: 2017-10-08 13:07:28 +0000 (Sun, 08 Oct 2017) Log Message: ----------- first simple Promise.all cases are working Modified Paths: -------------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Promise.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/PromiseTest.java Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Promise.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Promise.java 2017-10-07 17:39:00 UTC (rev 14864) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Promise.java 2017-10-08 13:07:28 UTC (rev 14865) @@ -37,6 +37,7 @@ import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime; import net.sourceforge.htmlunit.corejs.javascript.Scriptable; import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; +import net.sourceforge.htmlunit.corejs.javascript.TopLevel; import net.sourceforge.htmlunit.corejs.javascript.Undefined; /** @@ -181,7 +182,16 @@ return; } + if (all_ != null) { + settleAll(window); + return; + } + settleThis(fulfilled, newValue, window); + } + + private void settleThis(final boolean fulfilled, final Object newValue, final Window window) { value_ = newValue; + if (fulfilled) { state_ = PromiseState.FULFILLED; } @@ -202,6 +212,24 @@ } } + private void settleAll(final Window window) { + final ArrayList<Object> values = new ArrayList<>(all_.length); + for (Promise promise : all_) { + if (promise.state_ == PromiseState.REJECTED) { + settleThis(false, promise.value_, window); + return; + } + else if (promise.state_ == PromiseState.PENDING) { + return; + } + values.add(promise.value_); + } + + final NativeArray jsValues = new NativeArray(values.toArray()); + ScriptRuntime.setBuiltinProtoAndParent(jsValues, window, TopLevel.Builtins.Array); + settleThis(true, jsValues, window); + } + /** * Returns a {@link Promise} that resolves when all of the promises in the iterable argument have resolved, * or rejects with the reason of the first passed promise that rejects. @@ -215,28 +243,33 @@ @JsxStaticFunction public static Promise all(final Context context, final Scriptable thisObj, final Object[] args, final Function function) { - final Promise promise = new Promise(); - promise.state_ = PromiseState.FULFILLED; + final Window window = getWindow(thisObj); + final Promise returnPromise = new Promise(window); + if (args.length == 0) { - promise.all_ = new Promise[0]; + returnPromise.all_ = new Promise[0]; } - else { + else if (args[0] instanceof NativeArray) { final NativeArray array = (NativeArray) args[0]; final int length = (int) array.getLength(); - promise.all_ = new Promise[length]; + returnPromise.all_ = new Promise[length]; for (int i = 0; i < length; i++) { final Object o = array.get(i); if (o instanceof Promise) { - promise.all_[i] = (Promise) o; + returnPromise.all_[i] = (Promise) o; + returnPromise.all_[i].dependentPromise_ = returnPromise; } else { - promise.all_[i] = resolve(null, thisObj, new Object[] {o}, null); + returnPromise.all_[i] = create(thisObj, new Object[] {o}, PromiseState.FULFILLED); } } } - promise.setParentScope(thisObj.getParentScope()); - promise.setPrototype(getWindow(thisObj).getPrototype(promise.getClass())); - return promise; + else { + // TODO + } + + returnPromise.settleAll(window); + return returnPromise; } /** Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/PromiseTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/PromiseTest.java 2017-10-07 17:39:00 UTC (rev 14864) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/PromiseTest.java 2017-10-08 13:07:28 UTC (rev 14865) @@ -14,9 +14,6 @@ */ package com.gargoylesoftware.htmlunit.javascript.host; -import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.CHROME; -import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.FF; - import org.junit.Test; import org.junit.runner.RunWith; import org.openqa.selenium.By; @@ -24,7 +21,6 @@ import com.gargoylesoftware.htmlunit.BrowserRunner; import com.gargoylesoftware.htmlunit.BrowserRunner.Alerts; -import com.gargoylesoftware.htmlunit.BrowserRunner.NotYetImplemented; import com.gargoylesoftware.htmlunit.WebDriverTestCase; /** @@ -101,14 +97,103 @@ final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); assertEquals(String.join("\n", getExpectedAlerts()), text); } + /** + * @throws Exception if an error occurs + */ + @Test + @Alerts(CHROME = { "function () { [native code] }", + "function () { [native code] }", + "[object Window]", + "done", "resolved value"}, + FF = { "function () {\n [native code]\n}", + "function () {\n [native code]\n}", + "[object Window]", + "done", "resolved value"}, + IE = {}) + public void constructor() throws Exception { + final String html = "<html>\n" + + "<head>\n" + + " <script>\n" + + " function test() {\n" + + " if (window.Promise) {\n" + + " var p = new Promise(function(resolve, reject) {\n" + + " log(resolve);\n" + + " log(reject);\n" + + " log(this);\n" + + " resolve('resolved value');\n" + + " });\n" + + " p.then(function(value) {log(value);});\n" + + " log('done');\n" + + " }\n" + + " }\n" + + "\n" + + " function log(x) {\n" + + " document.getElementById('log').value += x + '\\n';\n" + + " }\n" + + " </script>\n" + + "</head>\n" + + "<body onload='test()'>\n" + + " <textarea id='log' cols='80' rows='40'></textarea>\n" + + "</body>\n" + + "</html>"; + final WebDriver driver = loadPage2(html); + Thread.sleep(200); + final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); + assertEquals(String.join("\n", getExpectedAlerts()), text); + } + /** * @throws Exception if an error occurs */ @Test - @Alerts(DEFAULT = {"done", "Resolved"}, + @Alerts(DEFAULT = {"true", "true", "true"}, IE = {}) - public void resolve() throws Exception { + public void constructorWithoutFunction() throws Exception { + final String html = "<html>\n" + + "<head>\n" + + " <script>\n" + + " function test() {\n" + + " if (window.Promise) {\n" + + " try{\n" + + " var p = new Promise();\n" + + " log('done');\n" + + " } catch(e) { log(e instanceof TypeError); }\n" + + " try{\n" + + " var p = new Promise([1, 2, 4]);\n" + + " log('done');\n" + + " } catch(e) { log(e instanceof TypeError); }\n" + + " try{\n" + + " var original = Promise.resolve(42);\n" + + " var p = new Promise(original);\n" + + " log('done');\n" + + " } catch(e) { log(e instanceof TypeError); }\n" + + " }\n" + + " }\n" + + "\n" + + " function log(x) {\n" + + " document.getElementById('log').value += x + '\\n';\n" + + " }\n" + + " </script>\n" + + "</head>\n" + + "<body onload='test()'>\n" + + " <textarea id='log' cols='80' rows='40'></textarea>\n" + + "</body>\n" + + "</html>"; + + final WebDriver driver = loadPage2(html); + + final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); + assertEquals(String.join("\n", getExpectedAlerts()), text); + } + + /** + * @throws Exception if an error occurs + */ + @Test + @Alerts(DEFAULT = {"done", "Rejected"}, + IE = {}) + public void reject() throws Exception { final String html = "<html>\n" + "<head>\n" @@ -115,10 +200,10 @@ + " <script>\n" + " function test() {\n" + " if (window.Promise) {\n" - + " Promise.resolve('Resolved').then(function(value) {\n" + + " Promise.reject('Rejected').then(function(value) {\n" + + " log('failure');\n" + + " }, function(value) {\n" + " log(value);\n" - + " }, function(value) {\n" - + " log('failure');\n" + " });\n" + " log('done');\n" + " }\n" @@ -143,9 +228,9 @@ * @throws Exception if an error occurs */ @Test - @Alerts(DEFAULT = {"done", "Rejected"}, + @Alerts(DEFAULT = {"done", "false", "[object Promise]"}, IE = {}) - public void reject() throws Exception { + public void rejectPromise() throws Exception { final String html = "<html>\n" + "<head>\n" @@ -152,12 +237,15 @@ + " <script>\n" + " function test() {\n" + " if (window.Promise) {\n" - + " Promise.reject('Rejected').then(function(value) {\n" - + " log('failure');\n" + + " var original = Promise.reject(42);\n" + + " var cast = Promise.reject(original);\n" + + " cast.then(function(v) {\n" + + " log('failure');\n" + " }, function(value) {\n" - + " log(value);\n" + + " log(value);\n" + " });\n" + " log('done');\n" + + " log(original === cast);\n" + " }\n" + " }\n" + " function log(x) {\n" @@ -180,9 +268,9 @@ * @throws Exception if an error occurs */ @Test - @Alerts(DEFAULT = {"done", "false", "[object Promise]"}, + @Alerts(DEFAULT = {"done", "Resolved"}, IE = {}) - public void rejectPromise() throws Exception { + public void resolve() throws Exception { final String html = "<html>\n" + "<head>\n" @@ -189,15 +277,12 @@ + " <script>\n" + " function test() {\n" + " if (window.Promise) {\n" - + " var original = Promise.reject(42);\n" - + " var cast = Promise.reject(original);\n" - + " cast.then(function(v) {\n" - + " log('failure');\n" + + " Promise.resolve('Resolved').then(function(value) {\n" + + " log(value);\n" + " }, function(value) {\n" - + " log(value);\n" + + " log('failure');\n" + " });\n" + " log('done');\n" - + " log(original === cast);\n" + " }\n" + " }\n" + " function log(x) {\n" @@ -729,7 +814,7 @@ * @throws Exception if an error occurs */ @Test - @Alerts(DEFAULT = "Success", + @Alerts(DEFAULT = {"done", "Success"}, IE = "") public void thenInsideEventHandler() throws Exception { final String html = "<html>\n" @@ -736,29 +821,35 @@ + "<head>\n" + " <script>\n" + " function test() {\n" - + " if(!window.Promise) {\n" - + " return;\n" + + " if (window.Promise) {\n" + + " document.getElementById('btn1').onclick = function() {\n" + + " new Promise(function(resolve, reject) {\n" + + " window.setTimeout( function() {\n" + + " resolve('Success');\n" + + " }, 20);\n" + + " }).then(function(value) {\n" + + " log(value);\n" + + " });\n" + + " log('done');\n" + + " };\n" + " }\n" - + " document.getElementById('btn1').onclick = function() {\n" - + " new Promise(function(resolve, reject) {\n" - + " window.setTimeout( function() {\n" - + " resolve('Success');\n" - + " }, 0);\n" - + " }).then(function(value) {\n" - + " document.title += value;\n" - + " });\n" - + " };\n" + " }\n" + + "\n" + + " function log(x) {\n" + + " document.getElementById('log').value += x + '\\n';\n" + + " }\n" + " </script>\n" + "</head>\n" + "<body onload='test()'>\n" - + "<button id='btn1'>BTN1</button>\n" + + " <button id='btn1'>BTN1</button>\n" + + " <textarea id='log' cols='80' rows='40'></textarea>\n" + "</body>\n" - + "</html>"; + + "</html>\n"; final WebDriver driver = loadPage2(html); driver.findElement(By.id("btn1")).click(); Thread.sleep(200); - assertEquals(getExpectedAlerts()[0], driver.getTitle()); + final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); + assertEquals(String.join("\n", getExpectedAlerts()), text); } /** @@ -767,10 +858,9 @@ * @throws Exception if an error occurs */ @Test - @Alerts(DEFAULT = {"object", "3", "3,1337,foo"}, + @Alerts(DEFAULT = {"done", "object", "3", "3,1337,Success"}, IE = {}) - @NotYetImplemented({CHROME, FF}) - public void all() throws Exception { + public void allAsync() throws Exception { final String html = "<html>\n" + "<head>\n" + " <script>\n" @@ -779,14 +869,17 @@ + " var p1 = Promise.resolve(3);\n" + " var p2 = 1337;\n" + " var p3 = new Promise(function(resolve, reject) {\n" - + " setTimeout(resolve, 100, \"foo\");\n" + + " window.setTimeout( function() {\n" + + " resolve('Success');\n" + + " }, 20);\n" + " });\n" + "\n" - + " Promise.all([ p1, p2, p3 ]).then(function(values) {\n" + + " Promise.all([p1, p2, p3]).then(function(values) {\n" + " log(typeof values);\n" + " log(values.length);\n" + " log(values);\n" + " });\n" + + " log('done');\n" + " }\n" + " }\n" + "\n" @@ -806,44 +899,45 @@ } /** + * Test for Bug #1780. + * * @throws Exception if an error occurs */ @Test - @Alerts(CHROME = { "function () { [native code] }", - "function () { [native code] }", - "[object Window]", - "done", "resolved value"}, - FF = { "function () {\n [native code]\n}", - "function () {\n [native code]\n}", - "[object Window]", - "done", "resolved value"}, + @Alerts(DEFAULT = {"done", "string", "Failed"}, IE = {}) - public void constructor() throws Exception { + public void allRejectAsync() throws Exception { final String html = "<html>\n" - + "<head>\n" - + " <script>\n" - + " function test() {\n" - + " if (window.Promise) {\n" - + " var p = new Promise(function(resolve, reject) {\n" - + " log(resolve);\n" - + " log(reject);\n" - + " log(this);\n" - + " resolve('resolved value');\n" - + " });\n" - + " p.then(function(value) {log(value);});\n" - + " log('done');\n" - + " }\n" - + " }\n" - + "\n" - + " function log(x) {\n" - + " document.getElementById('log').value += x + '\\n';\n" - + " }\n" - + " </script>\n" - + "</head>\n" - + "<body onload='test()'>\n" - + " <textarea id='log' cols='80' rows='40'></textarea>\n" - + "</body>\n" - + "</html>"; + + "<head>\n" + + " <script>\n" + + " function test() {\n" + + " if (window.Promise) {\n" + + " var p1 = Promise.resolve(3);\n" + + " var p2 = 1337;\n" + + " var p3 = new Promise(function(resolve, reject) {\n" + + " window.setTimeout( function() {\n" + + " reject('Failed');\n" + + " }, 20);\n" + + " });\n" + + "\n" + + " Promise.all([p1, p2, p3]).then(function(value) {\n" + + " log('failure');\n" + + " }, function(value) {\n" + + " log(typeof value);\n" + + " log(value);\n" + + " });\n" + + " log('done');\n" + + " }\n" + + " }\n" + + "\n" + + " function log(x) {\n" + + " document.getElementById('log').value += x + '\\n';\n" + + " }\n" + + "</script></head>\n" + + "<body onload='test()'>\n" + + " <textarea id='log' cols='80' rows='40'></textarea>\n" + + "</body>\n" + + "</html>\n"; final WebDriver driver = loadPage2(html); Thread.sleep(200); @@ -850,48 +944,4 @@ final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); assertEquals(String.join("\n", getExpectedAlerts()), text); } - - /** - * @throws Exception if an error occurs - */ - @Test - @Alerts(DEFAULT = {"true", "true", "true"}, - IE = {}) - public void constructorWithoutFunction() throws Exception { - final String html = "<html>\n" - + "<head>\n" - + " <script>\n" - + " function test() {\n" - + " if (window.Promise) {\n" - + " try{\n" - + " var p = new Promise();\n" - + " log('done');\n" - + " } catch(e) { log(e instanceof TypeError); }\n" - + " try{\n" - + " var p = new Promise([1, 2, 4]);\n" - + " log('done');\n" - + " } catch(e) { log(e instanceof TypeError); }\n" - + " try{\n" - + " var original = Promise.resolve(42);\n" - + " var p = new Promise(original);\n" - + " log('done');\n" - + " } catch(e) { log(e instanceof TypeError); }\n" - + " }\n" - + " }\n" - + "\n" - + " function log(x) {\n" - + " document.getElementById('log').value += x + '\\n';\n" - + " }\n" - + " </script>\n" - + "</head>\n" - + "<body onload='test()'>\n" - + " <textarea id='log' cols='80' rows='40'></textarea>\n" - + "</body>\n" - + "</html>"; - - final WebDriver driver = loadPage2(html); - - final String text = driver.findElement(By.id("log")).getAttribute("value").trim().replaceAll("\r", ""); - assertEquals(String.join("\n", getExpectedAlerts()), text); - } } |