json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 830 861f15abe944
child 920 117b732d42d0
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
     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 
    57 /**
    58  *
    59  * @author Jaroslav Tulach
    60  */
    61 @Model(className="KnockoutModel", properties={
    62     @Property(name="name", type=String.class),
    63     @Property(name="results", type=String.class, array = true),
    64     @Property(name="numbers", type=int.class, array = true),
    65     @Property(name="callbackCount", type=int.class),
    66     @Property(name="people", type=PersonImpl.class, array = true),
    67     @Property(name="enabled", type=boolean.class),
    68     @Property(name="latitude", type=double.class),
    69     @Property(name="choice", type=KnockoutTest.Choice.class),
    70     @Property(name="archetype", type=ArchetypeData.class),
    71     @Property(name="archetypes", type=ArchetypeData.class, array = true),
    72 }) 
    73 public final class KnockoutTest {
    74     private KnockoutModel js;
    75     
    76     enum Choice {
    77         A, B;
    78     }
    79     
    80     @ComputedProperty static List<Integer> resultLengths(List<String> results) {
    81         Integer[] arr = new Integer[results.size()];
    82         for (int i = 0; i < arr.length; i++) {
    83             arr[i] = results.get(i).length();
    84         }
    85         return Arrays.asList(arr);
    86     }
    87     
    88     @KOTest public void modifyValueAssertChangeInModelOnEnum() throws Throwable {
    89         Object exp = Utils.exposeHTML(KnockoutTest.class, 
    90             "Latitude: <input id='input' data-bind=\"value: choice\"></input>\n"
    91         );
    92         try {
    93 
    94             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
    95             m.setChoice(Choice.A);
    96             m.applyBindings();
    97 
    98             String v = getSetInput(null);
    99             assert "A".equals(v) : "Value is really A: " + v;
   100 
   101             getSetInput("B");
   102             triggerEvent("input", "change");
   103 
   104             assert Choice.B == m.getChoice(): "Enum property updated: " + m.getChoice();
   105         } catch (Throwable t) {
   106             throw t;
   107         } finally {
   108             Utils.exposeHTML(KnockoutTest.class, "");
   109         }
   110     }
   111     
   112     @KOTest public void modifyValueAssertChangeInModelOnDouble() throws Throwable {
   113         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   114             "Latitude: <input id='input' data-bind=\"value: latitude\"></input>\n"
   115         );
   116         try {
   117 
   118             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   119             m.setLatitude(50.5);
   120             m.applyBindings();
   121 
   122             String v = getSetInput(null);
   123             assert "50.5".equals(v) : "Value is really 50.5: " + v;
   124 
   125             getSetInput("49.5");
   126             triggerEvent("input", "change");
   127 
   128             assert 49.5 == m.getLatitude() : "Double property updated: " + m.getLatitude();
   129         } catch (Throwable t) {
   130             throw t;
   131         } finally {
   132             Utils.exposeHTML(KnockoutTest.class, "");
   133         }
   134     }
   135     
   136     @KOTest public void modifyValueAssertChangeInModelOnBoolean() throws Throwable {
   137         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   138             "Latitude: <input id='input' data-bind=\"value: enabled\"></input>\n"
   139         );
   140         try {
   141 
   142             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   143             m.setEnabled(true);
   144             m.applyBindings();
   145 
   146             String v = getSetInput(null);
   147             assert "true".equals(v) : "Value is really true: " + v;
   148 
   149             getSetInput("false");
   150             triggerEvent("input", "change");
   151 
   152             assert false == m.isEnabled(): "Boolean property updated: " + m.isEnabled();
   153         } catch (Throwable t) {
   154             throw t;
   155         } finally {
   156             Utils.exposeHTML(KnockoutTest.class, "");
   157         }
   158     }
   159     
   160     @KOTest public void modifyValueAssertChangeInModel() throws Exception {
   161         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   162             "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
   163             "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
   164             "<button id=\"hello\">Say Hello!</button>\n"
   165         );
   166         try {
   167 
   168             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   169             m.setName("Kukuc");
   170             m.applyBindings();
   171 
   172             String v = getSetInput(null);
   173             assert "Kukuc".equals(v) : "Value is really kukuc: " + v;
   174 
   175             getSetInput("Jardo");
   176             triggerEvent("input", "change");
   177 
   178             assert "Jardo".equals(m.getName()) : "Name property updated: " + m.getName();
   179         } finally {
   180             Utils.exposeHTML(KnockoutTest.class, "");
   181         }
   182     }
   183     
   184     private static String getSetSelected(int index, Object value) throws Exception {
   185         String s = "var index = arguments[0];\n"
   186         + "var value = arguments[1];\n"
   187         + "var n = window.document.getElementById('input'); \n "
   188         + "if (value != null) {\n"
   189         + "  n.options[index].value = 'me'; \n"
   190         + "  n.value = 'me'; \n"
   191         + "  ko.dataFor(n.options[index]).archetype(value); // haven't found better way to trigger ko change yet \n"
   192         + "} \n "
   193         + "var op = n.options[n.selectedIndex]; \n"
   194         + "return op ? op.text : n.selectedIndex;\n";
   195         Object ret = Utils.executeScript(
   196             KnockoutTest.class,
   197             s, index, value
   198         );
   199         return ret == null ? null : ret.toString();
   200     }
   201     
   202     @Model(className = "ArchetypeData", properties = {
   203         @Property(name = "artifactId", type = String.class),
   204         @Property(name = "groupId", type = String.class),
   205         @Property(name = "version", type = String.class),
   206         @Property(name = "name", type = String.class),
   207         @Property(name = "description", type = String.class),
   208         @Property(name = "url", type = String.class),
   209     })
   210     static class ArchModel {
   211     }
   212     
   213     @KOTest public void selectWorksOnModels() throws Exception {
   214         if (js == null) {
   215             Utils.exposeHTML(KnockoutTest.class, 
   216                 "<select id='input' data-bind=\"options: archetypes,\n" +
   217 "                       optionsText: 'name',\n" +
   218 "                       value: archetype\">\n" +
   219 "                  </select>\n" +
   220 ""
   221             );
   222             
   223             js = Models.bind(new KnockoutModel(), newContext());
   224             js.getArchetypes().add(new ArchetypeData("ko4j", "org.netbeans.html", "0.8.3", "ko4j", "ko4j", null));
   225             js.getArchetypes().add(new ArchetypeData("crud", "org.netbeans.html", "0.8.3", "crud", "crud", null));
   226             js.getArchetypes().add(new ArchetypeData("3rd", "org.netbeans.html", "0.8.3", "3rd", "3rd", null));
   227             js.setArchetype(js.getArchetypes().get(1));
   228             js.applyBindings();
   229             
   230             String v = getSetSelected(0, null);
   231             assert "crud".equals(v) : "Second index (e.g. crud) is selected: " + v;
   232             
   233             String sel = getSetSelected(2, Models.toRaw(js.getArchetypes().get(2)));
   234             assert "3rd".equals(sel) : "3rd is selected now: " + sel;
   235         }
   236         
   237         if (js.getArchetype() != js.getArchetypes().get(2)) {
   238             throw new InterruptedException();
   239         }
   240         
   241         Utils.exposeHTML(KnockoutTest.class, "");
   242     }
   243     
   244     @KOTest public void modifyValueAssertAsyncChangeInModel() throws Exception {
   245         if (js == null) {
   246             Utils.exposeHTML(KnockoutTest.class, 
   247                 "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
   248                 "Your name: <input id='input' data-bind=\"value: name\"></input>\n" +
   249                 "<button id=\"hello\">Say Hello!</button>\n"
   250             );
   251             
   252             js = Models.bind(new KnockoutModel(), newContext());
   253             js.setName("Kukuc");
   254             js.applyBindings();
   255             
   256             String v = getSetInput(null);
   257             assert "Kukuc".equals(v) : "Value is really kukuc: " + v;
   258             
   259             Timer t = new Timer("Set to Jardo");
   260             t.schedule(new TimerTask() {
   261                 @Override
   262                 public void run() {
   263                     js.setName("Jardo");
   264                 }
   265             }, 1);
   266         }
   267         
   268         String v = getSetInput(null);
   269         if (!"Jardo".equals(v)) {
   270             throw new InterruptedException();
   271         }
   272         
   273         Utils.exposeHTML(KnockoutTest.class, "");
   274     }
   275     
   276     private static String getSetInput(String value) throws Exception {
   277         String s = "var value = arguments[0];\n"
   278         + "var n = window.document.getElementById('input'); \n "
   279         + "if (value != null) n['value'] = value; \n "
   280         + "return n['value'];";
   281         Object ret = Utils.executeScript(
   282             KnockoutTest.class,
   283             s, value
   284         );
   285         return ret == null ? null : ret.toString();
   286     }
   287     
   288     public static void triggerEvent(String id, String ev) throws Exception {
   289         Utils.executeScript(
   290             KnockoutTest.class,
   291             "ko.utils.triggerEvent(window.document.getElementById(arguments[0]), arguments[1]);",
   292             id, ev
   293         );
   294     }
   295     
   296     @KOTest public void displayContentOfArray() throws Exception {
   297         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   298             "<ul id='ul' data-bind='foreach: results'>\n"
   299             + "  <li data-bind='text: $data, click: $root.call'/>\n"
   300             + "</ul>\n"
   301         );
   302         try {
   303             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   304             m.getResults().add("Ahoj");
   305             m.applyBindings();
   306 
   307             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   308             assert cnt == 1 : "One child, but was " + cnt;
   309 
   310             m.getResults().add("Hi");
   311 
   312             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   313             assert cnt == 2 : "Two children now, but was " + cnt;
   314 
   315             triggerChildClick("ul", 1);
   316 
   317             assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
   318             assert "Hi".equals(m.getName()) : "We got callback from 2nd child " + m.getName();
   319         } finally {
   320             Utils.exposeHTML(KnockoutTest.class, "");
   321         }
   322     }
   323     
   324     @KOTest public void displayContentOfAsyncArray() throws Exception {
   325         if (js == null) {
   326             Utils.exposeHTML(KnockoutTest.class, 
   327                 "<ul id='ul' data-bind='foreach: results'>\n"
   328                 + "  <li data-bind='text: $data, click: $root.call'/>\n"
   329                 + "</ul>\n"
   330             );
   331             js = Models.bind(new KnockoutModel(), newContext());
   332             js.getResults().add("Ahoj");
   333             js.applyBindings();
   334 
   335             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   336             assert cnt == 1 : "One child, but was " + cnt;
   337             
   338             Timer t = new Timer("add to array");
   339             t.schedule(new TimerTask() {
   340                 @Override
   341                 public void run() {
   342                     js.getResults().add("Hi");
   343                 }
   344             }, 1);
   345         }
   346 
   347 
   348         int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   349         if (cnt != 2) {
   350             throw new InterruptedException();
   351         }
   352 
   353         try {
   354             triggerChildClick("ul", 1);
   355 
   356             assert 1 == js.getCallbackCount() : "One callback " + js.getCallbackCount();
   357             assert "Hi".equals(js.getName()) : "We got callback from 2nd child " + js.getName();
   358         } finally {
   359             Utils.exposeHTML(KnockoutTest.class, "");
   360         }
   361     }
   362     
   363     @KOTest public void displayContentOfComputedArray() throws Exception {
   364         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   365             "<ul id='ul' data-bind='foreach: bothNames'>\n"
   366             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   367             + "</ul>\n"
   368         );
   369         try {
   370             Pair m = Models.bind(new Pair("First", "Last", null), newContext());
   371             m.applyBindings();
   372 
   373             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   374             assert cnt == 2 : "Two children now, but was " + cnt;
   375 
   376             triggerChildClick("ul", 1);
   377 
   378             assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
   379             
   380             m.setLastName("Verylast");
   381 
   382             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   383             assert cnt == 2 : "Two children now, but was " + cnt;
   384             
   385             triggerChildClick("ul", 1);
   386 
   387             assert "Verylast".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
   388             
   389         } finally {
   390             Utils.exposeHTML(KnockoutTest.class, "");
   391         }
   392     }
   393     
   394     @KOTest public void displayContentOfComputedArrayOnASubpair() throws Exception {
   395         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   396               "<div data-bind='with: next'>\n"
   397             + "<ul id='ul' data-bind='foreach: bothNames'>\n"
   398             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   399             + "</ul>"
   400             + "</div>\n"
   401         );
   402         try {
   403             final BrwsrCtx ctx = newContext();
   404             Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), ctx);
   405             m.applyBindings();
   406 
   407             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   408             assert cnt == 2 : "Two children now, but was " + cnt;
   409 
   410             triggerChildClick("ul", 1);
   411             
   412             assert PairModel.ctx == ctx : "Context remains the same";
   413 
   414             assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
   415         } finally {
   416             Utils.exposeHTML(KnockoutTest.class, "");
   417         }
   418     }
   419     
   420     @KOTest public void displayContentOfComputedArrayOnComputedASubpair() throws Exception {
   421         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   422               "<div data-bind='with: nextOne'>\n"
   423             + "<ul id='ul' data-bind='foreach: bothNames'>\n"
   424             + "  <li data-bind='text: $data, click: $root.assignFirstName'/>\n"
   425             + "</ul>"
   426             + "</div>\n"
   427         );
   428         try {
   429             Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
   430             m.applyBindings();
   431 
   432             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   433             assert cnt == 2 : "Two children now, but was " + cnt;
   434 
   435             triggerChildClick("ul", 1);
   436 
   437             assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
   438         } finally {
   439             Utils.exposeHTML(KnockoutTest.class, "");
   440         }
   441     }
   442 
   443     @KOTest public void checkBoxToBooleanBinding() throws Exception {
   444         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   445             "<input type='checkbox' id='b' data-bind='checked: enabled'></input>\n"
   446         );
   447         try {
   448             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   449             m.applyBindings();
   450 
   451             assert !m.isEnabled() : "Is disabled";
   452 
   453             triggerClick("b");
   454 
   455             assert m.isEnabled() : "Now the model is enabled";
   456         } finally {
   457             Utils.exposeHTML(KnockoutTest.class, "");
   458         }
   459     }
   460     
   461     
   462     
   463     @KOTest public void displayContentOfDerivedArray() throws Exception {
   464         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   465             "<ul id='ul' data-bind='foreach: cmpResults'>\n"
   466             + "  <li><b data-bind='text: $data'></b></li>\n"
   467             + "</ul>\n"
   468         );
   469         try {
   470             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   471             m.getResults().add("Ahoj");
   472             m.applyBindings();
   473 
   474             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   475             assert cnt == 1 : "One child, but was " + cnt;
   476 
   477             m.getResults().add("hello");
   478 
   479             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   480             assert cnt == 2 : "Two children now, but was " + cnt;
   481         } finally {
   482             Utils.exposeHTML(KnockoutTest.class, "");
   483         }
   484     }
   485     
   486     @KOTest public void displayContentOfArrayOfPeople() throws Exception {
   487         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   488             "<ul id='ul' data-bind='foreach: people'>\n"
   489             + "  <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
   490             + "</ul>\n"
   491         );
   492         try {
   493             final BrwsrCtx c = newContext();
   494             KnockoutModel m = Models.bind(new KnockoutModel(), c);
   495 
   496             final Person first = Models.bind(new Person(), c);
   497             first.setFirstName("first");
   498             m.getPeople().add(first);
   499 
   500             m.applyBindings();
   501 
   502             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   503             assert cnt == 1 : "One child, but was " + cnt;
   504 
   505             final Person second = Models.bind(new Person(), c);
   506             second.setFirstName("second");
   507             m.getPeople().add(second);
   508 
   509             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   510             assert cnt == 2 : "Two children now, but was " + cnt;
   511 
   512             triggerChildClick("ul", 1);
   513 
   514             assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
   515 
   516             cnt = Utils.countChildren(KnockoutTest.class, "ul");
   517             assert cnt == 1 : "Again one child, but was " + cnt;
   518 
   519             String txt = childText("ul", 0);
   520             assert "first".equals(txt) : "Expecting 'first': " + txt;
   521 
   522             first.setFirstName("changed");
   523 
   524             txt = childText("ul", 0);
   525             assert "changed".equals(txt) : "Expecting 'changed': " + txt;
   526         } finally {
   527             Utils.exposeHTML(KnockoutTest.class, "");
   528         }
   529     }
   530     
   531     @ComputedProperty
   532     static Person firstPerson(List<Person> people) {
   533         return people.isEmpty() ? null : people.get(0);
   534     }
   535     
   536     @KOTest public void accessFirstPersonWithOnFunction() throws Exception {
   537         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   538             "<p id='ul' data-bind='with: firstPerson'>\n"
   539             + "  <span data-bind='text: firstName, click: changeSex'></span>\n"
   540             + "</p>\n"
   541         );
   542         try {
   543             trasfertToFemale();
   544         } finally {
   545             Utils.exposeHTML(KnockoutTest.class, "");
   546         }
   547     }
   548     
   549     @KOTest public void onPersonFunction() throws Exception {
   550         Object exp = Utils.exposeHTML(KnockoutTest.class, 
   551             "<ul id='ul' data-bind='foreach: people'>\n"
   552             + "  <li data-bind='text: $data.firstName, click: changeSex'></li>\n"
   553             + "</ul>\n"
   554         );
   555         try {
   556             trasfertToFemale();
   557         } finally {
   558             Utils.exposeHTML(KnockoutTest.class, "");
   559         }
   560     }
   561     
   562     private void trasfertToFemale() throws Exception {
   563         KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   564 
   565         final Person first = Models.bind(new Person(), newContext());
   566         first.setFirstName("first");
   567         first.setSex(Sex.MALE);
   568         m.getPeople().add(first);
   569 
   570 
   571         m.applyBindings();
   572 
   573         int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   574         assert cnt == 1 : "One child, but was " + cnt;
   575 
   576 
   577         triggerChildClick("ul", 0);
   578 
   579         assert first.getSex() == Sex.FEMALE : "Transverted to female: " + first.getSex();
   580     }
   581     
   582     @KOTest public void stringArrayModificationVisible() throws Exception {
   583         Object exp = Utils.exposeHTML(KnockoutTest.class,
   584                 "<div>\n"
   585                 + "<ul id='ul' data-bind='foreach: results'>\n"
   586                 + "  <li data-bind='text: $data'></li>\n"
   587                 + "</ul>\n"
   588               + "</div>\n"
   589         );
   590         try {
   591             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   592             m.getResults().add("Ahoj");
   593             m.getResults().add("Hello");
   594             m.applyBindings();
   595             
   596             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   597             assert cnt == 2 : "Two children " + cnt;
   598             
   599             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
   600             assert arr instanceof Object[] : "Got back an array: " + arr;
   601             final int len = ((Object[])arr).length;
   602             
   603             assert len == 3 : "Three elements in the array " + len;
   604             
   605             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   606             assert newCnt == 3 : "Three children in the DOM: " + newCnt;
   607             
   608             assert m.getResults().size() == 3 : "Three java strings: " + m.getResults();
   609         } finally {
   610             Utils.exposeHTML(KnockoutTest.class, "");
   611         }
   612     }
   613 
   614     @KOTest public void intArrayModificationVisible() throws Exception {
   615         Object exp = Utils.exposeHTML(KnockoutTest.class,
   616                 "<div>\n"
   617                 + "<ul id='ul' data-bind='foreach: numbers'>\n"
   618                 + "  <li data-bind='text: $data'></li>\n"
   619                 + "</ul>\n"
   620               + "</div>\n"
   621         );
   622         try {
   623             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   624             m.getNumbers().add(1);
   625             m.getNumbers().add(31);
   626             m.applyBindings();
   627             
   628             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   629             assert cnt == 2 : "Two children " + cnt;
   630             
   631             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "numbers", 42);
   632             assert arr instanceof Object[] : "Got back an array: " + arr;
   633             final int len = ((Object[])arr).length;
   634             
   635             assert len == 3 : "Three elements in the array " + len;
   636             
   637             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   638             assert newCnt == 3 : "Three children in the DOM: " + newCnt;
   639             
   640             assert m.getNumbers().size() == 3 : "Three java ints: " + m.getNumbers();
   641             assert m.getNumbers().get(2) == 42 : "Meaning of world: " + m.getNumbers();
   642         } finally {
   643             Utils.exposeHTML(KnockoutTest.class, "");
   644         }
   645     }
   646 
   647     @KOTest public void derivedIntArrayModificationVisible() throws Exception {
   648         Object exp = Utils.exposeHTML(KnockoutTest.class,
   649                 "<div>\n"
   650                 + "<ul id='ul' data-bind='foreach: resultLengths'>\n"
   651                 + "  <li data-bind='text: $data'></li>\n"
   652                 + "</ul>\n"
   653               + "</div>\n"
   654         );
   655         try {
   656             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   657             m.getResults().add("Ahoj");
   658             m.getResults().add("Hello");
   659             m.applyBindings();
   660             
   661             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   662             assert cnt == 2 : "Two children " + cnt;
   663             
   664             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "results", "Hi");
   665             assert arr instanceof Object[] : "Got back an array: " + arr;
   666             final int len = ((Object[])arr).length;
   667             
   668             assert len == 3 : "Three elements in the array " + len;
   669             
   670             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   671             assert newCnt == 3 : "Three children in the DOM: " + newCnt;
   672             
   673             assert m.getResultLengths().size() == 3 : "Three java ints: " + m.getResultLengths();
   674             assert m.getResultLengths().get(2) == 2 : "Size is two: " + m.getResultLengths();
   675         } finally {
   676             Utils.exposeHTML(KnockoutTest.class, "");
   677         }
   678     }
   679     
   680     @KOTest public void archetypeArrayModificationVisible() throws Exception {
   681         Object exp = Utils.exposeHTML(KnockoutTest.class,
   682                 "<div>\n"
   683                 + "<ul id='ul' data-bind='foreach: archetypes'>\n"
   684                 + "  <li data-bind='text: artifactId'></li>\n"
   685                 + "</ul>\n"
   686               + "</div>\n"
   687         );
   688         try {
   689             KnockoutModel m = Models.bind(new KnockoutModel(), newContext());
   690             m.applyBindings();
   691             
   692             int cnt = Utils.countChildren(KnockoutTest.class, "ul");
   693             assert cnt == 0 : "No children " + cnt;
   694             
   695             Object arr = Utils.addChildren(KnockoutTest.class, "ul", "archetypes", new ArchetypeData("aid", "gid", "v", "n", "d", "u"));
   696             assert arr instanceof Object[] : "Got back an array: " + arr;
   697             final int len = ((Object[])arr).length;
   698             
   699             assert len == 1 : "One element in the array " + len;
   700             
   701             int newCnt = Utils.countChildren(KnockoutTest.class, "ul");
   702             assert newCnt == 1 : "One child in the DOM: " + newCnt;
   703             
   704             assert m.getArchetypes().size() == 1 : "One archetype: " + m.getArchetypes();
   705             assert m.getArchetypes().get(0) != null : "Not null: " + m.getArchetypes();
   706             assert m.getArchetypes().get(0).getArtifactId().equals("aid") : "'aid' == " + m.getArchetypes();
   707         } finally {
   708             Utils.exposeHTML(KnockoutTest.class, "");
   709         }
   710     }
   711 
   712     @Function
   713     static void call(KnockoutModel m, String data) {
   714         m.setName(data);
   715         m.setCallbackCount(m.getCallbackCount() + 1);
   716     }
   717 
   718     @Function
   719     static void removePerson(KnockoutModel model, Person data) {
   720         model.setCallbackCount(model.getCallbackCount() + 1);
   721         model.getPeople().remove(data);
   722     }
   723     
   724     
   725     @ComputedProperty
   726     static String helloMessage(String name) {
   727         return "Hello " + name + "!";
   728     }
   729     
   730     @ComputedProperty
   731     static List<String> cmpResults(List<String> results) {
   732         return results;
   733     }
   734     
   735     private static void triggerClick(String id) throws Exception {
   736         String s = "var id = arguments[0];"
   737             + "var e = window.document.getElementById(id);\n "
   738             + "if (e.checked) throw 'It should not be checked yet: ' + e;\n "
   739             + "var ev = window.document.createEvent('MouseEvents');\n "
   740             + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
   741             + "e.dispatchEvent(ev);\n "
   742             + "if (!e.checked) {\n"
   743             + "  e.checked = true;\n "
   744             + "  e.dispatchEvent(ev);\n "
   745             + "}\n";
   746         Utils.executeScript(
   747             KnockoutTest.class,
   748             s, id);
   749     }
   750     private static void triggerChildClick(String id, int pos) throws Exception {
   751         String s = 
   752             "var id = arguments[0]; var pos = arguments[1];\n" +
   753             "var e = window.document.getElementById(id);\n " +
   754             "var ev = window.document.createEvent('MouseEvents');\n " +
   755             "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n " +
   756             "var list = e.childNodes;\n" +
   757             "var cnt = -1;\n" + 
   758             "for (var i = 0; i < list.length; i++) {\n" + 
   759             "  if (list[i].nodeType == 1) cnt++;\n" + 
   760             "  if (cnt == pos) return list[i].dispatchEvent(ev);\n" + 
   761             "}\n" + 
   762             "return null;\n";
   763         Utils.executeScript(
   764             KnockoutTest.class,
   765             s, id, pos);
   766     }
   767 
   768     private static String childText(String id, int pos) throws Exception {
   769         String s = 
   770             "var id = arguments[0]; var pos = arguments[1];" +
   771             "var e = window.document.getElementById(id);\n" +
   772             "var list = e.childNodes;\n" +
   773             "var cnt = -1;\n" + 
   774             "for (var i = 0; i < list.length; i++) {\n" + 
   775             "  if (list[i].nodeType == 1) cnt++;\n" + 
   776             "  if (cnt == pos) return list[i].innerHTML;\n" + 
   777             "}\n" + 
   778             "return null;\n";
   779         return (String)Utils.executeScript(
   780             KnockoutTest.class,
   781             s, id, pos);
   782     }
   783 
   784     private static BrwsrCtx newContext() {
   785         return Utils.newContext(KnockoutTest.class);
   786     }
   787 }