Making the archetype much simpler - one model class, one HTML page is easier to change into something real
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 08 Sep 2013 15:14:36 +0200
changeset 2865e439fb334d4
parent 285 f5052612793b
child 287 20e0763e6cac
Making the archetype much simpler - one model class, one HTML page is easier to change into something real
ko-archetype/src/main/resources/archetype-resources/pom.xml
ko-archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java
ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java
ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java
ko-archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html
ko-archetype/src/main/resources/archetype-resources/src/main/webapp/pages/twitterExample.css
ko-archetype/src/main/resources/archetype-resources/src/test/java/DataModelTest.java
ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java
     1.1 --- a/ko-archetype/src/main/resources/archetype-resources/pom.xml	Sat Sep 07 20:31:23 2013 +0200
     1.2 +++ b/ko-archetype/src/main/resources/archetype-resources/pom.xml	Sun Sep 08 15:14:36 2013 +0200
     1.3 @@ -129,4 +129,28 @@
     1.4        <scope>test</scope>
     1.5      </dependency>
     1.6    </dependencies>
     1.7 +  <profiles>
     1.8 +      <profile>
     1.9 +          <id>jdk18</id>
    1.10 +          <activation>
    1.11 +              <jdk>1.8</jdk>
    1.12 +          </activation>
    1.13 +          <build>
    1.14 +              <plugins>
    1.15 +                  <plugin>
    1.16 +                      <groupId>org.apache.maven.plugins</groupId>
    1.17 +                      <artifactId>maven-compiler-plugin</artifactId>
    1.18 +                      <version>2.3.2</version>
    1.19 +                      <configuration>
    1.20 +                        <source>1.7</source>
    1.21 +                        <target>1.8</target>
    1.22 +                        <compilerArguments>
    1.23 +                            <profile>compact1</profile>
    1.24 +                        </compilerArguments>
    1.25 +                      </configuration>
    1.26 +                  </plugin>
    1.27 +              </plugins>
    1.28 +          </build>
    1.29 +      </profile>
    1.30 +  </profiles>  
    1.31  </project>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java	Sun Sep 08 15:14:36 2013 +0200
     2.3 @@ -0,0 +1,31 @@
     2.4 +package ${package};
     2.5 +
     2.6 +import net.java.html.json.ComputedProperty;
     2.7 +import net.java.html.json.Function;
     2.8 +import net.java.html.json.Model;
     2.9 +import net.java.html.json.Property;
    2.10 +
    2.11 +/** Model annotation generates class Data with 
    2.12 + * one message property, boolean property and read only words property
    2.13 + */
    2.14 +@Model(className = "Data", properties = {
    2.15 +    @Property(name = "message", type = String.class),
    2.16 +    @Property(name = "on", type = boolean.class)
    2.17 +})
    2.18 +final class DataModel {
    2.19 +    @ComputedProperty static java.util.List<String> words(String message) {
    2.20 +        String[] arr = new String[6];
    2.21 +        String[] words = message == null ? new String[0] : message.split(" ", 6);
    2.22 +        for (int i = 0; i < 6; i++) {
    2.23 +            arr[i] = words.length > i ? words[i] : "!";
    2.24 +        }
    2.25 +        return java.util.Arrays.asList(arr);
    2.26 +    }
    2.27 +    
    2.28 +    @Function static void turnOn(Data model) {
    2.29 +        model.setOn(true);
    2.30 +    }
    2.31 +    @Function static void turnOff(Data model) {
    2.32 +        model.setOn(false);
    2.33 +    }
    2.34 +}
     3.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java	Sat Sep 07 20:31:23 2013 +0200
     3.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java	Sun Sep 08 15:14:36 2013 +0200
     3.3 @@ -9,9 +9,19 @@
     3.4      public static void main(String... args) throws Exception {
     3.5          BrowserBuilder.newBrowser().
     3.6              loadPage("pages/index.html").
     3.7 -            loadClass(TwitterClient.class).
     3.8 -            invoke("initialize", args).
     3.9 +            loadClass(Main.class).
    3.10 +            invoke("onPageLoad", args).
    3.11              showAndWait();
    3.12          System.exit(0);
    3.13      }
    3.14 +
    3.15 +    /**
    3.16 +     * Called when the page is ready.
    3.17 +     */
    3.18 +    public static void onPageLoad(String... args) throws Exception {
    3.19 +        Data d = new Data();
    3.20 +        d.setMessage("Hello World from HTML and Java!");
    3.21 +        d.applyBindings();
    3.22 +    }
    3.23 +    
    3.24  }
     4.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java	Sat Sep 07 20:31:23 2013 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,178 +0,0 @@
     4.4 -package ${package};
     4.5 -
     4.6 -import java.util.Arrays;
     4.7 -import java.util.List;
     4.8 -import net.java.html.json.ComputedProperty;
     4.9 -import net.java.html.json.Function;
    4.10 -import net.java.html.json.Model;
    4.11 -import net.java.html.json.OnPropertyChange;
    4.12 -import net.java.html.json.OnReceive;
    4.13 -import net.java.html.json.Property;
    4.14 -
    4.15 -@Model(className="TwitterModel", properties={
    4.16 -    @Property(name="savedLists", type=Tweeters.class, array = true),
    4.17 -    @Property(name="activeTweetersName", type=String.class),
    4.18 -    @Property(name="activeTweeters", type=String.class, array = true),
    4.19 -    @Property(name="userNameToAdd", type=String.class),
    4.20 -    @Property(name="loading", type=boolean.class),
    4.21 -    @Property(name="currentTweets", type=Tweet.class, array = true)
    4.22 -})
    4.23 -public class TwitterClient {
    4.24 -    @Model(className = "Tweeters", properties = {
    4.25 -        @Property(name="name", type = String.class),
    4.26 -        @Property(name="userNames", type = String.class, array = true)
    4.27 -    })
    4.28 -    static class Twttrs {
    4.29 -    }
    4.30 -    @Model(className = "Tweet", properties = {
    4.31 -        @Property(name = "from_user", type = String.class),
    4.32 -        @Property(name = "from_user_id", type = int.class),
    4.33 -        @Property(name = "profile_image_url", type = String.class),
    4.34 -        @Property(name = "text", type = String.class),
    4.35 -        @Property(name = "created_at", type = String.class),
    4.36 -    })
    4.37 -    static final class Twt {
    4.38 -        @ComputedProperty static String html(String text) {
    4.39 -            StringBuilder sb = new StringBuilder(320);
    4.40 -            for (int pos = 0;;) {
    4.41 -                int http = text.indexOf("http", pos);
    4.42 -                if (http == -1) {
    4.43 -                    sb.append(text.substring(pos));
    4.44 -                    return sb.toString();
    4.45 -                }
    4.46 -                int spc = text.indexOf(' ', http);
    4.47 -                if (spc == -1) {
    4.48 -                    spc = text.length();
    4.49 -                }
    4.50 -                sb.append(text.substring(pos, http));
    4.51 -                String url = text.substring(http, spc);
    4.52 -                sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
    4.53 -                pos = spc;
    4.54 -            }
    4.55 -        }
    4.56 -        
    4.57 -        @ComputedProperty static String userUrl(String from_user) {
    4.58 -            return "http://twitter.com/" + from_user;
    4.59 -        }
    4.60 -    }
    4.61 -    @Model(className = "TwitterQuery", properties = {
    4.62 -        @Property(array = true, name = "results", type = Twt.class)
    4.63 -    })
    4.64 -    public static final class TwttrQr {
    4.65 -    }
    4.66 -    
    4.67 -    @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
    4.68 -    static void queryTweets(TwitterModel page, TwitterQuery q) {
    4.69 -        page.getCurrentTweets().clear();
    4.70 -        page.getCurrentTweets().addAll(q.getResults());
    4.71 -        page.setLoading(false);
    4.72 -    }
    4.73 -    
    4.74 -    @OnPropertyChange("activeTweetersName")
    4.75 -    static void changeTweetersList(TwitterModel model) {
    4.76 -        Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());        
    4.77 -        model.getActiveTweeters().clear();
    4.78 -        model.getActiveTweeters().addAll(people.getUserNames());
    4.79 -    }
    4.80 -    
    4.81 -    @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
    4.82 -    static void refreshTweets(TwitterModel model) {
    4.83 -        StringBuilder sb = new StringBuilder();
    4.84 -        sb.append("rpp=25&q=");
    4.85 -        String sep = "";
    4.86 -        for (String p : model.getActiveTweeters()) {
    4.87 -            sb.append(sep);
    4.88 -            sb.append("from:");
    4.89 -            sb.append(p);
    4.90 -            sep = " OR ";
    4.91 -        }
    4.92 -        model.setLoading(true);
    4.93 -        model.queryTweets("http://search.twitter.com", sb.toString());
    4.94 -    }
    4.95 -    
    4.96 -    public static void initialize(String... args) {
    4.97 -        final TwitterModel model = new TwitterModel();
    4.98 -        final List<Tweeters> svdLst = model.getSavedLists();
    4.99 -        svdLst.add(newTweeters("API Design", "JaroslavTulach"));
   4.100 -        svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
   4.101 -        svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
   4.102 -        svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
   4.103 -        svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
   4.104 -
   4.105 -        model.setActiveTweetersName("NetBeans");
   4.106 -
   4.107 -        model.applyBindings();
   4.108 -    }
   4.109 -    
   4.110 -    @ComputedProperty
   4.111 -    static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
   4.112 -        Tweeters tw = findByName(savedLists, activeTweetersName);
   4.113 -        if (activeTweeters == null) {
   4.114 -            return false;
   4.115 -        }
   4.116 -        return !tw.getUserNames().equals(activeTweeters);
   4.117 -    }
   4.118 -    
   4.119 -    @ComputedProperty
   4.120 -    static int activeTweetersCount(List<String> activeTweeters) {
   4.121 -        return activeTweeters.size();
   4.122 -    }
   4.123 -    
   4.124 -    @ComputedProperty
   4.125 -    static boolean userNameToAddIsValid(
   4.126 -        String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
   4.127 -    ) {
   4.128 -        return userNameToAdd != null && 
   4.129 -            userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
   4.130 -            !activeTweeters.contains(userNameToAdd);
   4.131 -    }
   4.132 -    
   4.133 -    @Function
   4.134 -    static void deleteList(TwitterModel model) {
   4.135 -        final List<Tweeters> sl = model.getSavedLists();
   4.136 -        sl.remove(findByName(sl, model.getActiveTweetersName()));
   4.137 -        if (sl.isEmpty()) {
   4.138 -            final Tweeters t = new Tweeters();
   4.139 -            t.setName("New");
   4.140 -            sl.add(t);
   4.141 -        }
   4.142 -        model.setActiveTweetersName(sl.get(0).getName());
   4.143 -    }
   4.144 -    
   4.145 -    @Function
   4.146 -    static void saveChanges(TwitterModel model) {
   4.147 -        Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
   4.148 -        int indx = model.getSavedLists().indexOf(t);
   4.149 -        if (indx != -1) {
   4.150 -            t.setName(model.getActiveTweetersName());
   4.151 -            t.getUserNames().clear();
   4.152 -            t.getUserNames().addAll(model.getActiveTweeters());
   4.153 -        }
   4.154 -    }
   4.155 -    
   4.156 -    @Function
   4.157 -    static void addUser(TwitterModel model) {
   4.158 -        String n = model.getUserNameToAdd();
   4.159 -        model.getActiveTweeters().add(n);
   4.160 -    }
   4.161 -    @Function
   4.162 -    static void removeUser(String data, TwitterModel model) {
   4.163 -        model.getActiveTweeters().remove(data);
   4.164 -    }
   4.165 -    
   4.166 -    private static Tweeters findByName(List<Tweeters> list, String name) {
   4.167 -        for (Tweeters l : list) {
   4.168 -            if (l.getName() != null && l.getName().equals(name)) {
   4.169 -                return l;
   4.170 -            }
   4.171 -        }
   4.172 -        return list.isEmpty() ? new Tweeters() : list.get(0);
   4.173 -    }
   4.174 -    
   4.175 -    private static Tweeters newTweeters(String listName, String... userNames) {
   4.176 -        Tweeters t = new Tweeters();
   4.177 -        t.setName(listName);
   4.178 -        t.getUserNames().addAll(Arrays.asList(userNames));
   4.179 -        return t;
   4.180 -    }
   4.181 -}
     5.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html	Sat Sep 07 20:31:23 2013 +0200
     5.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html	Sun Sep 08 15:14:36 2013 +0200
     5.3 @@ -1,79 +1,58 @@
     5.4 -<?xml version="1.0" encoding="UTF-8"?>
     5.5 +<!DOCTYPE html>
     5.6 +<html>
     5.7 +    <head>
     5.8 +        <title></title>
     5.9 +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    5.10  
    5.11 -<!--
    5.12 -    Copied from knockout.js Twitter example:
    5.13 -    http://knockoutjs.com/examples/twitter.html
    5.14 --->
    5.15 +        <style type="text/css">
    5.16 +            @-webkit-keyframes spin {
    5.17 +                0% { -webkit-transform: rotate(0deg); }
    5.18 +                100% { -webkit-transform: rotate(360deg); }
    5.19 +            }
    5.20  
    5.21 -<!DOCTYPE html>
    5.22 -<html xmlns="http://www.w3.org/1999/xhtml">
    5.23 -    <head>
    5.24 -        <title>Knockout in Java Twitter</title>
    5.25 +            .rotate {
    5.26 +                -webkit-animation-name: spin;
    5.27 +                -webkit-animation-duration: 3s;
    5.28 +                -webkit-animation-iteration-count: infinite;
    5.29 +                -webkit-animation-direction: alternate;
    5.30 +            }
    5.31 +
    5.32 +            #scene {
    5.33 +                position: relative;
    5.34 +                top: 60px;
    5.35 +                text-align: center;
    5.36 +            }
    5.37 +            
    5.38 +            #words span {
    5.39 +                border: 1px solid #ccc;
    5.40 +                background: rgba(255,255,155,0.8);
    5.41 +                text-align: center;
    5.42 +                font-size: 30px;                
    5.43 +                -webkit-box-shadow: inset 0 0 40px rgba(0,0,0,0.4);
    5.44 +                position: absolute;
    5.45 +            }
    5.46 +
    5.47 +            #words span:nth-child(1) { left: 45%; top: 0px; }
    5.48 +            #words span:nth-child(2) { left: 25%; top: 100px; }
    5.49 +            #words span:nth-child(3) { left: 65%; top: 100px; }
    5.50 +            #words span:nth-child(4) { left: 10%; top: 200px; }
    5.51 +            #words span:nth-child(5) { left: 45%; top: 200px; }
    5.52 +            #words span:nth-child(6) { left: 80%; top: 200px; }
    5.53 +            
    5.54 +        </style>
    5.55 +
    5.56      </head>
    5.57      <body>
    5.58 -        <link href='twitterExample.css' rel='Stylesheet' ></link>
    5.59 -        
    5.60 -        <style type='text/css'>
    5.61 -           .liveExample select { height: 1.7em; }
    5.62 -           .liveExample button { height: 2em; }
    5.63 -        </style>
    5.64 -        
    5.65 -        
    5.66 -        <h2>Knockout in Java Twitter</h2>
    5.67 -        
    5.68 -        <p>
    5.69 -        This code is based on original 
    5.70 -        <a href="http://knockoutjs.com/examples/twitter.html">knockout.js
    5.71 -        Twitter example</a> and
    5.72 -        uses almost unmodified HTML page. It just changes the model. The model
    5.73 -        is written in Java language with the help of 
    5.74 -        <a href="http://bck2brwsr.apidesign.org/javadoc/net.java.html.json/">
    5.75 -            Knockout/Java binding library
    5.76 -        </a>. The Java source code has about 180 lines and seems more 
    5.77 -        dense and shorter than the original JavaScript model.
    5.78 -        </p>
    5.79 -        <p>
    5.80 -        The project executes in real Java virtual
    5.81 -        machine and renders using JavaFX's WebView.
    5.82 -        </p>
    5.83 -        
    5.84 -        <div class='liveExample'>
    5.85 -            <div class='configuration'>
    5.86 -                <div class='listChooser'>
    5.87 -                    <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
    5.88 -                    <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button> 
    5.89 -                    <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
    5.90 -                </div>
    5.91 +        <h1>Words Demo</h1>
    5.92 +        <input data-bind="value: message, valueUpdate: 'afterkeydown'" size="80">
    5.93 +        <br>
    5.94 +        <button data-bind="enable: !on(), click: $root.turnOn">Start</button>
    5.95 +        <button data-bind="enable: on, click: $root.turnOff">Stop</button>
    5.96  
    5.97 -                <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
    5.98 -                <div class='currentUsers' >
    5.99 -                    <ul data-bind='foreach: activeTweeters'>
   5.100 -                        <li>
   5.101 -                            <button data-bind='click: $root.removeUser'>Remove</button>
   5.102 -                            <div data-bind='text: $data'> </div>
   5.103 -                        </li>
   5.104 -                    </ul>
   5.105 -                </div>
   5.106 -
   5.107 -                <form data-bind='submit: addUser'>
   5.108 -                    <label>Add user:</label>
   5.109 -                    <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
   5.110 -                    <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
   5.111 -                </form>
   5.112 -            </div>
   5.113 -            <div class='tweets'>
   5.114 -                <div class='loadingIndicator' data-bind="visible: loading">Loading...</div>
   5.115 -                <table data-bind='foreach: currentTweets' width='100%'>
   5.116 -                    <tr>
   5.117 -                        <td><img data-bind='attr: { src: profile_image_url }' /></td>
   5.118 -                        <td>
   5.119 -                            <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
   5.120 -                            <span data-bind='html: html'> </span>
   5.121 -                            <div class='tweetInfo' data-bind='text: created_at'> </div>
   5.122 -                        </td>
   5.123 -                    </tr>
   5.124 -                </table>
   5.125 -            </div>
   5.126 +        <div id="scene">
   5.127 +            <span id="words" data-bind="foreach: words">
   5.128 +                <span data-bind="text: $data, css: { 'rotate' : $root.on } "></span>
   5.129 +            </span>
   5.130          </div>
   5.131      </body>
   5.132  </html>
     6.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/webapp/pages/twitterExample.css	Sat Sep 07 20:31:23 2013 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,32 +0,0 @@
     6.4 -/*
     6.5 -    Copied from knockout.js Twitter example:
     6.6 -    http://knockoutjs.com/examples/twitter.html
     6.7 -*/
     6.8 -
     6.9 -.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
    6.10 -.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
    6.11 -.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
    6.12 -.tweets table { border-width: 0;}
    6.13 -.tweets tr { vertical-align: top; }
    6.14 -.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
    6.15 -.tweets img { width: 4em; }
    6.16 -.tweetInfo { color: Gray; font-size: 0.9em; }
    6.17 -.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
    6.18 -input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
    6.19 -
    6.20 -.listChooser select, .listChooser button { vertical-align:top; }
    6.21 -.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
    6.22 -.listChooser button { width: 19%; height:1.68em; float:right; }
    6.23 -
    6.24 -.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
    6.25 -.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
    6.26 -.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
    6.27 -.currentUsers li { height: 2.4em; font-size: 1.2em; background-color: #A7D0E3; border: 1px solid gray; margin-bottom: 0.3em; -webkit-border-radius: 5px; -moz-border-radius: 5px; -webkit-box-shadow: 0 0.2em 0.5em gray; -moz-box-shadow: 0 0.2em 0.5em gray; }
    6.28 -.currentUsers li div { padding: 0.6em; }
    6.29 -.currentUsers li:hover { background-color: #EEC; }
    6.30 -
    6.31 -.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
    6.32 -.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
    6.33 -.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
    6.34 -
    6.35 -.loadingIndicator { position: absolute; top: 0.1em; left: 0.1em; font: 0.8em Arial; background-color: #229; color: White; padding: 0.2em 0.5em 0.2em 0.5em; }
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/test/java/DataModelTest.java	Sun Sep 08 15:14:36 2013 +0200
     7.3 @@ -0,0 +1,16 @@
     7.4 +package ${package};
     7.5 +
     7.6 +import static org.testng.Assert.*;
     7.7 +import org.testng.annotations.Test;
     7.8 +
     7.9 +public class DataModelTest {
    7.10 +    @Test public void areHelloWorldTwoWords() {
    7.11 +        Data model = new Data();
    7.12 +        model.setMessage("Hello World!");
    7.13 +        
    7.14 +        java.util.List<String> arr = model.getWords();
    7.15 +        assertEquals(arr.size(), 6, "Six words always");
    7.16 +        assertEquals("Hello", arr.get(0), "Hello is the first word");
    7.17 +        assertEquals("World!", arr.get(1), "World is the second word");
    7.18 +    }
    7.19 +}
     8.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java	Sat Sep 07 20:31:23 2013 +0200
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,50 +0,0 @@
     8.4 -package ${package};
     8.5 -
     8.6 -import java.util.List;
     8.7 -import net.java.html.BrwsrCtx;
     8.8 -import net.java.html.json.Models;
     8.9 -import static org.testng.Assert.*;
    8.10 -import org.testng.annotations.BeforeMethod;
    8.11 -import org.testng.annotations.Test;
    8.12 -
    8.13 -/** We can unit test the TwitterModel smoothly.
    8.14 - */
    8.15 -public class TwitterClientTest {
    8.16 -    private TwitterModel model;
    8.17 -    
    8.18 -
    8.19 -    @BeforeMethod
    8.20 -    public void initModel() {
    8.21 -        model = Models.bind(new TwitterModel(), BrwsrCtx.EMPTY);
    8.22 -    }
    8.23 -
    8.24 -    @Test public void testIsValidToAdd() {
    8.25 -        model.setUserNameToAdd("Joe");
    8.26 -        Tweeters t = Models.bind(new Tweeters(), BrwsrCtx.EMPTY);
    8.27 -        t.setName("test");
    8.28 -        model.getSavedLists().add(t);
    8.29 -        model.setActiveTweetersName("test");
    8.30 -        
    8.31 -        assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
    8.32 -        TwitterClient.addUser(model);
    8.33 -        assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
    8.34 -        assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
    8.35 -        
    8.36 -        List<String> mod = model.getActiveTweeters();
    8.37 -        assertTrue(model.isHasUnsavedChanges(), "We have modifications");
    8.38 -        assertEquals(mod.size(), 1, "One element in the list");
    8.39 -        assertEquals(mod.get(0), "Joe", "Its name is Joe");
    8.40 -        
    8.41 -        assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
    8.42 -        
    8.43 -        TwitterClient.saveChanges(model);
    8.44 -        assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
    8.45 -        
    8.46 -        assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
    8.47 -    }
    8.48 -    
    8.49 -    @Test public void httpAtTheEnd() {
    8.50 -        String res = TwitterClient.Twt.html("Ahoj http://kuk");
    8.51 -        assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
    8.52 -    }
    8.53 -}