json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sun, 22 Nov 2015 21:18:35 +0100
changeset 1020 b5d5cbb44ce0
parent 990 581f50820e5e
child 1028 453e44c757ff
permissions -rw-r--r--
fromRaw(toRaw(...)) should yield object with same values
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package net.java.html.json.tests;
    44 
    45 import java.util.Arrays;
    46 import java.util.List;
    47 import java.util.Timer;
    48 import java.util.TimerTask;
    49 import net.java.html.BrwsrCtx;
    50 import net.java.html.json.ComputedProperty;
    51 import net.java.html.json.Function;
    52 import net.java.html.json.Model;
    53 import net.java.html.json.Models;
    54 import net.java.html.json.Property;
    55 import org.netbeans.html.json.tck.KOTest;
    56 import static net.java.html.json.tests.Utils.assertEquals;
    57 import static net.java.html.json.tests.Utils.assertNotNull;
    58 import static net.java.html.json.tests.Utils.assertTrue;
    59 import static net.java.html.json.tests.Utils.assertFalse;
    60 
    61 /**
    62  *
    63  * @author Jaroslav Tulach
    64  */
    65 @Model(className="KnockoutModel", targetId = "", properties={
    66     @Property(name="name", type=String.class),
    67     @Property(name="results", type=String.class, array = true),
    68     @Property(name="numbers", type=int.class, array = true),
    69     @Property(name="callbackCount", type=int.class),
    70     @Property(name="people", type=PersonImpl.class, array = true),
    71     @Property(name="enabled", type=boolean.class),
    72     @Property(name="latitude", type=double.class),
    73     @Property(name="choice", type=KnockoutTest.Choice.class),
    74     @Property(name="archetype", type=ArchetypeData.class),
    75     @Property(name="archetypes", type=ArchetypeData.class, array = true),
    76 }) 
    77 public final class KnockoutTest {
    78     private KnockoutModel js;
    79     
    80     enum Choice {
    81         A, B;
    82     }
    83     
    84     @ComputedProperty static List<Integer> resultLengths(List<String> results) {
    85         Integer[] arr = new Integer[results.size()];
    86         for (int i = 0; i < arr.length; i++) {
    87             arr[i] = results.get(i).length();
    88         }
    89         return Arrays.asList(arr);
    90     }
    91     
    92     @KOTest public void modifyValueAssertChangeInModelOnEnum() throws Throwable {
    93         Object exp = Utils.exposeHTML(KnockoutTest.class, 
    94             "Latitude: <input id='input' data-bind=\"value: choice\"></input>\n"
    95         );
    96         try {
    97 
    98             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
    99             m.setChoice(Choice.A);
   100             m.applyBindings();
   101 
   102             String v = getSetInput("input", null);
   103             assertEquals("A", v, "Value is really A: " + v);
   104 
   105             getSetInput("input", "B");
   106             triggerEvent("input", "change");
   107 
   108             assertEquals(Choice.B, m.getChoice(), "Enum property updated: " + m.getChoice());
   109         } catch (Throwable t) {
   110             throw t;
   111         } finally {
   112             Utils.exposeHTML(KnockoutTest.class, "");
   113         }
   114     }
   115 
   116 
   117     @KOTest public void modifyRadioValueOnEnum() throws Throwable {
   118         Object exp = Utils.exposeHTML(KnockoutTest.class,
   119             "<input id='i1' type=\"radio\" name=\"choice\" value=\"A\" data-bind=\"checked: choice\"></input>Right\n" +
   120             "<input id='input' type=\"radio\" name=\"choice\" value=\"B\" data-bind=\"checked: choice\"></input>Never\n" +
   121             "\n"
   122         );
   123         try {
   124 
   125             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   126             m.setChoice(Choice.B);
   127             m.applyBindings();
   128 
   129             assertFalse(isChecked("i1"), "B should be checked now");
   130             assertTrue(isChecked("input"), "B should be checked now");
   131 
   132             triggerEvent("i1", "click");
   133             assertEquals(Choice.A, m.getChoice(), "Switched to A");
   134             assertTrue(isChecked("i1"), "A should be checked now");
   135             assertFalse(isChecked("input"), "A should be checked now");
   136 
   137             triggerEvent("input", "click");
   138 
   139             assertEquals(Choice.B, m.getChoice(), "Enum property updated: " + m.getChoice());
   140         } catch (Throwable t) {
   141             throw t;
   142         } finally {
   143             Utils.exposeHTML(KnockoutTest.class, "");
   144         }
   145     }
   146     
   147     @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
   148         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   149             "Latitude: <input id='input' data-bind=\"value: latitude\"></input>\n"
   150         );
   151         try {
   152 
   153             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   154             m.setLatitude(50.5);
   155             m.applyBindings();
   156 
   157             String v = getSetInput("input", null);
   158             assertEquals("50.5", v, "Value is really 50.5: " + v);
   159 
   160             getSetInput("input", "49.5");
   161             triggerEvent("input", "change");
   162 
   163             assertEquals(49.5, m.getLatitude(), "Double property updated: " + m.getLatitude());
   164         } catch (Throwable t) {
   165             throw t;
   166         } finally {
   167             Utils.exposeHTML(KnockoutTest.class, "");
   168         }
   169     }
   170     
   171     @KOTest public void rawObject() {
   172         final BrwsrCtx ctx = newContext();
   173         Person p1 = Models.bind(new Person(), ctx);
   174         p1.setFirstName("Jarda");
   175         p1.setLastName("Tulach");
   176         Object raw = Models.toRaw(p1);
   177         Person p2 = Models.fromRaw(ctx, Person.class, raw);
   178         
   179         assertEquals(p2.getFirstName(), "Jarda", "First name");
   180         assertEquals(p2.getLastName(), "Tulach", "Last name");
   181     }
   182 
   183     @KOTest public void modifyComputedProperty() throws Throwable {
   184         Object exp = Utils.exposeHTML(KnockoutTest.class,
   185             "Full name: <div data-bind='with:firstPerson'>\n"
   186                 + "<input id='input' data-bind=\"value: fullName\"></input>\n"
   187                 + "</div>\n"
   188         );
   189         try {
   190             KnockoutModel m = new KnockoutModel();
   191             m.getPeople().add(new Person());
   192 
   193             m = Models.bind(m, newContext());
   194             m.getFirstPerson().setFirstName("Jarda");
   195             m.getFirstPerson().setLastName("Tulach");
   196             m.applyBindings();
   197 
   198             String v = getSetInput("input", null);
   199             assertEquals("Jarda Tulach", v, "Value: " + v);
   200 
   201             getSetInput("input", "Mickey Mouse");
   202             triggerEvent("input", "change");
   203 
   204             assertEquals("Mickey", m.getFirstPerson().getFirstName(), "First name updated");
   205             assertEquals("Mouse", m.getFirstPerson().getLastName(), "Last name updated");
   206         } catch (Throwable t) {
   207             throw t;
   208         } finally {
   209             Utils.exposeHTML(KnockoutTest.class, "");
   210         }
   211     }
   212     
   213     @KOTest public void modifyValueAssertChangeInModelOnBoolean() throws Throwable {
   214         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   215             "Latitude: <input id='input' data-bind=\"value: enabled\"></input>\n"
   216         );
   217         try {
   218 
   219             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   220             m.setEnabled(true);
   221             m.applyBindings();
   222 
   223             String v = getSetInput("input", null);
   224             assertEquals("true", v, "Value is really true: " + v);
   225 
   226             getSetInput("input", "false");
   227             triggerEvent("input", "change");
   228 
   229             assertFalse(m.isEnabled(), "Boolean property updated: " + m.isEnabled());
   230         } catch (Throwable t) {
   231             throw t;
   232         } finally {
   233             Utils.exposeHTML(KnockoutTest.class, "");
   234         }
   235     }
   236     
   237     @KOTest public void modifyValueAssertChangeInModel() throws Exception {
   238         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   239             "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
   240             "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
   241             "<button id=\"hello\">Say Hello!</button>\n"
   242         );
   243         try {
   244 
   245             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   246             m.setName("Kukuc");
   247             m.applyBindings();
   248 
   249             String v = getSetInput("input", null);
   250             assertEquals("Kukuc", v, "Value is really kukuc: " + v);
   251 
   252             getSetInput("input", "Jardo");
   253             triggerEvent("input", "change");
   254 
   255             assertEquals("Jardo", m.getName(), "Name property updated: " + m.getName());
   256         } finally {
   257             Utils.exposeHTML(KnockoutTest.class, "");
   258         }
   259     }
   260     
   261     private static String getSetSelected(int index, Object value) throws Exception {
   262         String s = "var index = arguments[0];\n"
   263         + "var value = arguments[1];\n"
   264         + "var n = window.document.getElementById('input'); \n "
   265         + "if (value != null) {\n"
   266         + "  n.options[index].value = 'me'; \n"
   267         + "  n.value = 'me'; \n"
   268         + "  ko.dataFor(n.options[index]).archetype(value); // haven't found better way to trigger ko change yet \n"
   269         + "} \n "
   270         + "var op = n.options[n.selectedIndex]; \n"
   271         + "return op ? op.text : n.selectedIndex;\n";
   272         Object ret = Utils.executeScript(
   273             KnockoutTest.class,
   274             s, index, value
   275         );
   276         return ret == null ? null : ret.toString();
   277     }
   278     
   279     @Model(className = "ArchetypeData", properties = {
   280         @Property(name = "artifactId", type = String.class),
   281         @Property(name = "groupId", type = String.class),
   282         @Property(name = "version", type = String.class),
   283         @Property(name = "name", type = String.class),
   284         @Property(name = "description", type = String.class),
   285         @Property(name = "url", type = String.class),
   286     })
   287     static class ArchModel {
   288     }
   289     
   290     @KOTest public void selectWorksOnModels() throws Exception {
   291         if (js == null) {
   292             Utils.exposeHTML(KnockoutTest.class, 
   293                 "<select id='input' data-bind=\"options: archetypes,\n" +
   294 "                       optionsText: 'name',\n" +
   295 "                       value: archetype\">\n" +
   296 "                  </select>\n" +
   297 ""
   298             );
   299             
   300             js = Models.bind(new KnockoutModel(), newContext());
   301             js.getArchetypes().add(new ArchetypeData("ko4j", "org.netbeans.html", "0.8.3", "ko4j", "ko4j", null));
   302             js.getArchetypes().add(new ArchetypeData("crud", "org.netbeans.html", "0.8.3", "crud", "crud", null));
   303             js.getArchetypes().add(new ArchetypeData("3rd", "org.netbeans.html", "0.8.3", "3rd", "3rd", null));
   304             js.setArchetype(js.getArchetypes().get(1));
   305             js.applyBindings();
   306             
   307             String v = getSetSelected(0, null);
   308             assertEquals("crud", v, "Second index (e.g. crud) is selected: " + v);
   309             
   310             String sel = getSetSelected(2, Models.toRaw(js.getArchetypes().get(2)));
   311             assertEquals("3rd", sel, "3rd is selected now: " + sel);
   312         }
   313         
   314         if (js.getArchetype() != js.getArchetypes().get(2)) {
   315             throw new InterruptedException();
   316         }
   317         
   318         Utils.exposeHTML(KnockoutTest.class, "");
   319     }
   320 
   321     @KOTest public void nestedObjectEqualsChange() throws Exception {
   322         nestedObjectEqualsChange(true);
   323     }
   324 
   325     @KOTest public void nestedObjectChange() throws Exception {
   326         nestedObjectEqualsChange(false);
   327     }
   328     private  void nestedObjectEqualsChange(boolean preApply) throws Exception {
   329         Utils.exposeHTML(KnockoutTest.class,
   330 "            <div data-bind='with: archetype'>\n" +
   331 "                <input id='input' data-bind='value: groupId'></input>\n" +
   332 "            </div>\n"
   333         );
   334 
   335         js = Models.bind(new KnockoutModel(), newContext());
   336         if (preApply) {
   337             js.applyBindings();
   338         }
   339         js.setArchetype(new ArchetypeData());
   340         js.getArchetype().setGroupId("org.netbeans.html");
   341         js.applyBindings();
   342 
   343         String v = getSetInput("input", null);
   344         assertEquals("org.netbeans.html", v, "groupId has been changed");
   345         Utils.exposeHTML(KnockoutTest.class, "");
   346     }
   347     
   348     @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
   349         if (js == null) {
   350             Utils.exposeHTML(KnockoutTest.class, 
   351                 "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
   352                 "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
   353                 "<button id=\"hello\">Say Hello!</button>\n"
   354             );
   355             
   356             js = Models.bind(new KnockoutModel(), newContext());
   357             js.setName("Kukuc");
   358             js.applyBindings();
   359             
   360             String v = getSetInput("input", null);
   361             assertEquals("Kukuc", v, "Value is really kukuc: " + v);
   362             
   363             Timer t = new Timer("Set to Jardo");
   364             t.schedule(new TimerTask() {
   365                 @Override
   366                 public void run() {
   367                     js.setName("Jardo");
   368                 }
   369             }, 1);
   370         }
   371         
   372         String v = getSetInput("input", null);
   373         if (!"Jardo".equals(v)) {
   374             throw new InterruptedException();
   375         }
   376         
   377         Utils.exposeHTML(KnockoutTest.class, "");
   378     }
   379     
   380     private static String getSetInput(String id, String value) throws Exception {
   381         String s = "var value = arguments[0];\n"
   382         + "var n = window.document.getElementById(arguments[1]); \n "
   383         + "if (value != null) n['value'] = value; \n "
   384         + "return n['value'];";
   385         Object ret = Utils.executeScript(
   386             KnockoutTest.class,
   387             s, value, id
   388         );
   389         return ret == null ? null : ret.toString();
   390     }
   391 
   392     private static boolean isChecked(String id) throws Exception {
   393         String s = ""
   394         + "var n = window.document.getElementById(arguments[0]); \n "
   395         + "return n['checked'];";
   396         Object ret = Utils.executeScript(
   397             KnockoutTest.class,
   398             s, id
   399         );
   400         return Boolean.TRUE.equals(ret);
   401     }
   402     
   403     public static void triggerEvent(String id, String ev) throws Exception {
   404         Utils.executeScript(
   405             KnockoutTest.class,
   406             "ko.utils.triggerEvent(window.document.getElementById(arguments[0]), arguments[1]);",
   407             id, ev
   408         );
   409     }
   410     
   411     @KOTest public void displayContentOfArray() throws Exception {
   412         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   413             "<ul id='ul' data-bind='foreach: results'>\n"
   414             + "  <li data-bind='text: $data, click: $root.call'/>\n"
   415             + "</ul>\n"
   416         );
   417         try {
   418             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   419             m.getResults().add("Ahoj");
   420             m.applyBindings();
   421 
   422             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   423             assertEquals(cnt, 1, "One child, but was " + cnt);
   424 
   425             m.getResults().add("Hi");
   426 
   427             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   428             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   429 
   430             triggerChildClick("ul", 1);
   431 
   432             assertEquals(1, m.getCallbackCount(), "One callback " + m.getCallbackCount());
   433             assertEquals("Hi", m.getName(), "We got callback from 2nd child " + m.getName());
   434         } finally {
   435             Utils.exposeHTML(KnockoutTest.class, "");
   436         }
   437     }
   438     
   439     @KOTest public void displayContentOfAsyncArray() throws Exception {
   440         if (js == null) {
   441             Utils.exposeHTML(KnockoutTest.class, 
   442                 "<ul id='ul' data-bind='foreach: results'>\n"
   443                 + "  <li data-bind='text: $data, click: $root.call'/>\n"
   444                 + "</ul>\n"
   445             );
   446             js = Models.bind(new KnockoutModel(), newContext());
   447             js.getResults().add("Ahoj");
   448             js.applyBindings();
   449 
   450             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   451             assertEquals(cnt, 1, "One child, but was " + cnt);
   452             
   453             Timer t = new Timer("add to array");
   454             t.schedule(new TimerTask() {
   455                 @Override
   456                 public void run() {
   457                     js.getResults().add("Hi");
   458                 }
   459             }, 1);
   460         }
   461 
   462 
   463         int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   464         if (cnt != 2) {
   465             throw new InterruptedException();
   466         }
   467 
   468         try {
   469             triggerChildClick("ul", 1);
   470 
   471             assertEquals(1, js.getCallbackCount(), "One callback " + js.getCallbackCount());
   472             assertEquals("Hi", js.getName(), "We got callback from 2nd child " + js.getName());
   473         } finally {
   474             Utils.exposeHTML(KnockoutTest.class, "");
   475         }
   476     }
   477     
   478     @KOTest public void displayContentOfComputedArray() throws Exception {
   479         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   480             "<ul id='ul' data-bind='foreach: bothNames'>\n"
   481             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   482             + "</ul>\n"
   483         );
   484         try {
   485             Pair m = Models.bind(new Pair("First", "Last", null), newContext());
   486             m.applyBindings();
   487 
   488             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   489             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   490 
   491             triggerChildClick("ul", 1);
   492 
   493             assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
   494             
   495             m.setLastName("Verylast");
   496 
   497             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   498             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   499             
   500             triggerChildClick("ul", 1);
   501 
   502             assertEquals("Verylast", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
   503             
   504         } finally {
   505             Utils.exposeHTML(KnockoutTest.class, "");
   506         }
   507     }
   508     
   509     @KOTest public void displayContentOfComputedArrayOnASubpair() throws Exception {
   510         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   511               "<div data-bind='with: next'>\n"
   512             + "<ul id='ul' data-bind='foreach: bothNames'>\n"
   513             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   514             + "</ul>"
   515             + "</div>\n"
   516         );
   517         try {
   518             final BrwsrCtx ctx = newContext();
   519             Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), ctx);
   520             m.applyBindings();
   521 
   522             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   523             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   524 
   525             triggerChildClick("ul", 1);
   526             
   527             assertEquals(PairModel.ctx, ctx, "Context remains the same");
   528 
   529             assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
   530         } finally {
   531             Utils.exposeHTML(KnockoutTest.class, "");
   532         }
   533     }
   534     
   535     @KOTest public void displayContentOfComputedArrayOnComputedASubpair() throws Exception {
   536         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   537               "<div data-bind='with: nextOne'>\n"
   538             + "<ul id='ul' data-bind='foreach: bothNames'>\n"
   539             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   540             + "</ul>"
   541             + "</div>\n"
   542         );
   543         try {
   544             Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
   545             m.applyBindings();
   546 
   547             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   548             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   549 
   550             triggerChildClick("ul", 1);
   551 
   552             assertEquals("Last", m.getFirstName(), "We got callback from 2nd child " + m.getFirstName());
   553         } finally {
   554             Utils.exposeHTML(KnockoutTest.class, "");
   555         }
   556     }
   557 
   558     @KOTest public void checkBoxToBooleanBinding() throws Exception {
   559         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   560             "<input type='checkbox' id='b' data-bind='checked: enabled'></input>\n"
   561         );
   562         try {
   563             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   564             m.applyBindings();
   565 
   566             assertFalse(m.isEnabled(), "Is disabled");
   567 
   568             triggerClick("b");
   569 
   570             assertTrue(m.isEnabled(), "Now the model is enabled");
   571         } finally {
   572             Utils.exposeHTML(KnockoutTest.class, "");
   573         }
   574     }
   575     
   576     
   577     
   578     @KOTest public void displayContentOfDerivedArray() throws Exception {
   579         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   580             "<ul id='ul' data-bind='foreach: cmpResults'>\n"
   581             + "  <li><b data-bind='text: $data'></b></li>\n"
   582             + "</ul>\n"
   583         );
   584         try {
   585             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   586             m.getResults().add("Ahoj");
   587             m.applyBindings();
   588 
   589             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   590             assertEquals(cnt, 1, "One child, but was " + cnt);
   591 
   592             m.getResults().add("hello");
   593 
   594             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   595             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   596         } finally {
   597             Utils.exposeHTML(KnockoutTest.class, "");
   598         }
   599     }
   600     
   601     @KOTest public void displayContentOfArrayOfPeople() throws Exception {
   602         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   603             "<ul id='ul' data-bind='foreach: people'>\n"
   604             + "  <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
   605             + "</ul>\n"
   606         );
   607         try {
   608             final BrwsrCtx c = newContext();
   609             KnockoutModel m = Models.bind(new KnockoutModel(), c);
   610 
   611             final Person first = Models.bind(new Person(), c);
   612             first.setFirstName("first");
   613             m.getPeople().add(first);
   614 
   615             m.applyBindings();
   616 
   617             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   618             assertEquals(cnt, 1, "One child, but was " + cnt);
   619 
   620             final Person second = Models.bind(new Person(), c);
   621             second.setFirstName("second");
   622             m.getPeople().add(second);
   623 
   624             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   625             assertEquals(cnt, 2, "Two children now, but was " + cnt);
   626 
   627             triggerChildClick("ul", 1);
   628 
   629             assertEquals(1, m.getCallbackCount(), "One callback " + m.getCallbackCount());
   630 
   631             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   632             assertEquals(cnt , 1, "Again one child, but was " + cnt);
   633 
   634             String txt = childText("ul", 0);
   635             assertEquals("first", txt, "Expecting 'first': " + txt);
   636 
   637             first.setFirstName("changed");
   638 
   639             txt = childText("ul", 0);
   640             assertEquals("changed", txt, "Expecting 'changed': " + txt);
   641         } finally {
   642             Utils.exposeHTML(KnockoutTest.class, "");
   643         }
   644     }
   645     
   646     @ComputedProperty
   647     static Person firstPerson(List<Person> people) {
   648         return people.isEmpty() ? null : people.get(0);
   649     }
   650     
   651     @KOTest public void accessFirstPersonWithOnFunction() throws Exception {
   652         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   653             "<p id='ul' data-bind='with: firstPerson'>\n"
   654             + "  <span data-bind='text: firstName, click: changeSex'></span>\n"
   655             + "</p>\n"
   656         );
   657         try {
   658             trasfertToFemale();
   659         } finally {
   660             Utils.exposeHTML(KnockoutTest.class, "");
   661         }
   662     }
   663     
   664     @KOTest public void onPersonFunction() throws Exception {
   665         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   666             "<ul id='ul' data-bind='foreach: people'>\n"
   667             + "  <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
   668             + "</ul>\n"
   669         );
   670         try {
   671             trasfertToFemale();
   672         } finally {
   673             Utils.exposeHTML(KnockoutTest.class, "");
   674         }
   675     }
   676     
   677     private void trasfertToFemale() throws Exception {
   678         KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   679 
   680         final Person first = Models.bind(new Person(), newContext());
   681         first.setFirstName("first");
   682         first.setSex(Sex.MALE);
   683         m.getPeople().add(first);
   684 
   685 
   686         m.applyBindings();
   687 
   688         int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   689         assertEquals(cnt, 1, "One child, but was " + cnt);
   690 
   691 
   692         triggerChildClick("ul", 0);
   693 
   694         assertEquals(first.getSex(), Sex.FEMALE, "Transverted to female: " + first.getSex());
   695     }
   696     
   697     @KOTest public void stringArrayModificationVisible() throws Exception {
   698         Object exp = Utils.exposeHTML(KnockoutTest.class,
   699                 "<div>\n"
   700                 + "<ul id='ul' data-bind='foreach: results'>\n"
   701                 + "  <li data-bind='text: $data'></li>\n"
   702                 + "</ul>\n"
   703               + "</div>\n"
   704         );
   705         try {
   706             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   707             m.getResults().add("Ahoj");
   708             m.getResults().add("Hello");
   709             m.applyBindings();
   710             
   711             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   712             assertEquals(cnt, 2, "Two children " + cnt);
   713             
   714             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
   715             assertTrue(arr instanceof Object[], "Got back an array: " + arr);
   716             final int len = ((Object[])arr).length;
   717             
   718             assertEquals(len, 3, "Three elements in the array " + len);
   719             
   720             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   721             assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
   722             
   723             assertEquals(m.getResults().size(), 3, "Three java strings: " + m.getResults());
   724         } finally {
   725             Utils.exposeHTML(KnockoutTest.class, "");
   726         }
   727     }
   728 
   729     @KOTest public void intArrayModificationVisible() throws Exception {
   730         Object exp = Utils.exposeHTML(KnockoutTest.class,
   731                 "<div>\n"
   732                 + "<ul id='ul' data-bind='foreach: numbers'>\n"
   733                 + "  <li data-bind='text: $data'></li>\n"
   734                 + "</ul>\n"
   735               + "</div>\n"
   736         );
   737         try {
   738             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   739             m.getNumbers().add(1);
   740             m.getNumbers().add(31);
   741             m.applyBindings();
   742             
   743             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   744             assertEquals(cnt, 2, "Two children " + cnt);
   745             
   746             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "numbers", 42);
   747             assertTrue(arr instanceof Object[], "Got back an array: " + arr);
   748             final int len = ((Object[])arr).length;
   749             
   750             assertEquals(len, 3, "Three elements in the array " + len);
   751             
   752             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   753             assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
   754             
   755             assertEquals(m.getNumbers().size(), 3, "Three java ints: " + m.getNumbers());
   756             assertEquals(m.getNumbers().get(2), 42, "Meaning of world: " + m.getNumbers());
   757         } finally {
   758             Utils.exposeHTML(KnockoutTest.class, "");
   759         }
   760     }
   761 
   762     @KOTest public void derivedIntArrayModificationVisible() throws Exception {
   763         Object exp = Utils.exposeHTML(KnockoutTest.class,
   764                 "<div>\n"
   765                 + "<ul id='ul' data-bind='foreach: resultLengths'>\n"
   766                 + "  <li data-bind='text: $data'></li>\n"
   767                 + "</ul>\n"
   768               + "</div>\n"
   769         );
   770         try {
   771             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   772             m.getResults().add("Ahoj");
   773             m.getResults().add("Hello");
   774             m.applyBindings();
   775             
   776             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   777             assertEquals(cnt, 2, "Two children " + cnt);
   778             
   779             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
   780             assertTrue(arr instanceof Object[], "Got back an array: " + arr);
   781             final int len = ((Object[])arr).length;
   782             
   783             assertEquals(len, 3, "Three elements in the array " + len);
   784             
   785             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   786             assertEquals(newCnt, 3, "Three children in the DOM: " + newCnt);
   787             
   788             assertEquals(m.getResultLengths().size(), 3, "Three java ints: " + m.getResultLengths());
   789             assertEquals(m.getResultLengths().get(2), 2, "Size is two: " + m.getResultLengths());
   790         } finally {
   791             Utils.exposeHTML(KnockoutTest.class, "");
   792         }
   793     }
   794     
   795     @KOTest public void archetypeArrayModificationVisible() throws Exception {
   796         Object exp = Utils.exposeHTML(KnockoutTest.class,
   797                 "<div>\n"
   798                 + "<ul id='ul' data-bind='foreach: archetypes'>\n"
   799                 + "  <li data-bind='text: artifactId'></li>\n"
   800                 + "</ul>\n"
   801               + "</div>\n"
   802         );
   803         try {
   804             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   805             m.applyBindings();
   806             
   807             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   808             assertEquals(cnt, 0, "No children " + cnt);
   809             
   810             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "archetypes", new ArchetypeData("aid", "gid", "v", "n", "d", "u"));
   811             assertTrue(arr instanceof Object[], "Got back an array: " + arr);
   812             final int len = ((Object[])arr).length;
   813             
   814             assertEquals(len, 1, "One element in the array " + len);
   815             
   816             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   817             assertEquals(newCnt, 1, "One child in the DOM: " + newCnt);
   818             
   819             assertEquals(m.getArchetypes().size(), 1, "One archetype: " + m.getArchetypes());
   820             assertNotNull(m.getArchetypes().get(0), "Not null: " + m.getArchetypes());
   821             assertEquals(m.getArchetypes().get(0).getArtifactId(), "aid", "'aid' == " + m.getArchetypes());
   822         } finally {
   823             Utils.exposeHTML(KnockoutTest.class, "");
   824         }
   825     }
   826 
   827     @Function
   828     static void call(KnockoutModel m, String data) {
   829         m.setName(data);
   830         m.setCallbackCount(m.getCallbackCount() + 1);
   831     }
   832 
   833     @Function
   834     static void removePerson(KnockoutModel model, Person data) {
   835         model.setCallbackCount(model.getCallbackCount() + 1);
   836         model.getPeople().remove(data);
   837     }
   838     
   839     
   840     @ComputedProperty
   841     static String helloMessage(String name) {
   842         return "Hello " + name + "!";
   843     }
   844     
   845     @ComputedProperty
   846     static List<String> cmpResults(List<String> results) {
   847         return results;
   848     }
   849     
   850     private static void triggerClick(String id) throws Exception {
   851         String s = "var id = arguments[0];"
   852             + "var e = window.document.getElementById(id);\n "
   853             + "if (e.checked) throw 'It should not be checked yet: ' + e;\n "
   854             + "var ev = window.document.createEvent('MouseEvents');\n "
   855             + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
   856             + "e.dispatchEvent(ev);\n "
   857             + "if (!e.checked) {\n"
   858             + "  e.checked = true;\n "
   859             + "  e.dispatchEvent(ev);\n "
   860             + "}\n";
   861         Utils.executeScript(
   862             KnockoutTest.class,
   863             s, id);
   864     }
   865     private static void triggerChildClick(String id, int pos) throws Exception {
   866         String s = 
   867             "var id = arguments[0]; var pos = arguments[1];\n" +
   868             "var e = window.document.getElementById(id);\n " +
   869             "var ev = window.document.createEvent('MouseEvents');\n " +
   870             "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n " +
   871             "var list = e.childNodes;\n" +
   872             "var cnt = -1;\n" + 
   873             "for (var i = 0; i < list.length; i++) {\n" + 
   874             "  if (list[i].nodeType == 1) cnt++;\n" + 
   875             "  if (cnt == pos) return list[i].dispatchEvent(ev);\n" + 
   876             "}\n" + 
   877             "return null;\n";
   878         Utils.executeScript(
   879             KnockoutTest.class,
   880             s, id, pos);
   881     }
   882 
   883     private static String childText(String id, int pos) throws Exception {
   884         String s = 
   885             "var id = arguments[0]; var pos = arguments[1];" +
   886             "var e = window.document.getElementById(id);\n" +
   887             "var list = e.childNodes;\n" +
   888             "var cnt = -1;\n" + 
   889             "for (var i = 0; i < list.length; i++) {\n" + 
   890             "  if (list[i].nodeType == 1) cnt++;\n" + 
   891             "  if (cnt == pos) return list[i].innerHTML;\n" + 
   892             "}\n" + 
   893             "return null;\n";
   894         return (String)Utils.executeScript(
   895             KnockoutTest.class,
   896             s, id, pos);
   897     }
   898 
   899     private static BrwsrCtx newContext() {
   900         return Utils.newContext(KnockoutTest.class);
   901     }
   902 }