Moving archetype tests into separate module - at that moment it is possible to use the previously generated archetype
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 13 May 2013 14:25:37 +0200
changeset 12025f04bdbc6ee1
parent 1201 b6fd8b9ccc7a
child 1203 eaa7c421a09e
Moving archetype tests into separate module - at that moment it is possible to use the previously generated archetype
ko-archetype-test/pom.xml
ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/ArchetypeVersionTest.java
ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/VerifyArchetypeTest.java
ko-archetype/pom.xml
ko-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
ko-archetype/src/main/resources/archetype-resources/src/main/java/App.java
ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java
ko-archetype/src/main/resources/archetype-resources/src/main/resources/index.html
ko-archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css
ko-archetype/src/main/resources/archetype-resources/src/test/java/AppTest.java
ko-archetype/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java
ko-archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java
ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java
ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java
ko-archetype/src/test/java/org/apidesign/html/archetype/ArchetypeVersionTest.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ko-archetype-test/pom.xml	Mon May 13 14:25:37 2013 +0200
     1.3 @@ -0,0 +1,37 @@
     1.4 +<?xml version="1.0"?>
     1.5 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
     1.6 +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     1.7 +    <modelVersion>4.0.0</modelVersion>
     1.8 +    <parent>
     1.9 +        <groupId>org.apidesign</groupId>
    1.10 +        <artifactId>html</artifactId>
    1.11 +        <version>0.3-SNAPSHOT</version>
    1.12 +    </parent>
    1.13 +    <groupId>org.apidesign.html</groupId>
    1.14 +    <artifactId>ko-archetype-test</artifactId>
    1.15 +    <version>0.3-SNAPSHOT</version>
    1.16 +    <name>Knockout 4 Java Archetype Test</name>
    1.17 +    <url>http://maven.apache.org</url>
    1.18 +    <description>Verifies the Knockout &amp; net.java.html.json archetype behaves properly.</description>
    1.19 +    <properties>
    1.20 +        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    1.21 +    </properties>
    1.22 +    <dependencies>
    1.23 +        <dependency>
    1.24 +            <groupId>${project.groupId}</groupId>
    1.25 +            <artifactId>knockout4j-archetype</artifactId>
    1.26 +            <version>0.3-SNAPSHOT</version>
    1.27 +        </dependency>
    1.28 +        <dependency>
    1.29 +            <groupId>org.testng</groupId>
    1.30 +            <artifactId>testng</artifactId>
    1.31 +            <scope>test</scope>
    1.32 +        </dependency>
    1.33 +        <dependency>                                
    1.34 +            <groupId>org.apache.maven.shared</groupId>
    1.35 +            <artifactId>maven-verifier</artifactId>
    1.36 +            <version>1.4</version>
    1.37 +            <scope>test</scope>
    1.38 +        </dependency>  
    1.39 +    </dependencies>
    1.40 +</project>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/ArchetypeVersionTest.java	Mon May 13 14:25:37 2013 +0200
     2.3 @@ -0,0 +1,106 @@
     2.4 +/**
     2.5 + * HTML via Java(tm) Language Bindings
     2.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.7 + *
     2.8 + * This program is free software: you can redistribute it and/or modify
     2.9 + * it under the terms of the GNU General Public License as published by
    2.10 + * the Free Software Foundation, version 2 of the License.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details. apidesign.org
    2.16 + * designates this particular file as subject to the
    2.17 + * "Classpath" exception as provided by apidesign.org
    2.18 + * in the License file that accompanied this code.
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License
    2.21 + * along with this program. Look for COPYING file in the top folder.
    2.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    2.23 + */
    2.24 +package org.apidesign.html.archetype.test;
    2.25 +
    2.26 +import java.io.IOException;
    2.27 +import java.net.URL;
    2.28 +import javax.xml.XMLConstants;
    2.29 +import javax.xml.parsers.DocumentBuilderFactory;
    2.30 +import javax.xml.parsers.ParserConfigurationException;
    2.31 +import javax.xml.xpath.XPathConstants;
    2.32 +import javax.xml.xpath.XPathExpression;
    2.33 +import javax.xml.xpath.XPathExpressionException;
    2.34 +import javax.xml.xpath.XPathFactory;
    2.35 +import javax.xml.xpath.XPathFactoryConfigurationException;
    2.36 +import org.testng.annotations.Test;
    2.37 +import static org.testng.Assert.*;
    2.38 +import org.testng.annotations.BeforeClass;
    2.39 +import org.w3c.dom.Document;
    2.40 +import org.w3c.dom.NodeList;
    2.41 +import org.xml.sax.SAXException;
    2.42 +
    2.43 +/**
    2.44 + *
    2.45 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.46 + */
    2.47 +public class ArchetypeVersionTest {
    2.48 +    private String version;
    2.49 +    
    2.50 +    public ArchetypeVersionTest() {
    2.51 +    }
    2.52 +    
    2.53 +    @BeforeClass public void readCurrentVersion() throws Exception {
    2.54 +        version = findCurrentVersion();
    2.55 +        assertFalse(version.isEmpty(), "There should be some version string");
    2.56 +    }
    2.57 +    
    2.58 +
    2.59 +    @Test public void testComparePomDepsVersions() throws Exception {
    2.60 +        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
    2.61 +        URL r = l.getResource("archetype-resources/pom.xml");
    2.62 +        assertNotNull(r, "Archetype pom found");
    2.63 +        
    2.64 +        final XPathFactory fact = XPathFactory.newInstance();
    2.65 +        XPathExpression xp2 = fact.newXPath().compile(
    2.66 +            "//properties/net.java.html.version/text()"
    2.67 +        );
    2.68 +        
    2.69 +        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
    2.70 +        String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
    2.71 +
    2.72 +        assertEquals(arch, version, "net.java.html.json dependency needs to be on latest version");
    2.73 +    }
    2.74 +    
    2.75 +    @Test public void testNbActions() throws Exception {
    2.76 +        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
    2.77 +        URL r = l.getResource("archetype-resources/nbactions.xml");
    2.78 +        assertNotNull(r, "Archetype nb file found");
    2.79 +        
    2.80 +        final XPathFactory fact = XPathFactory.newInstance();
    2.81 +        XPathExpression xp2 = fact.newXPath().compile(
    2.82 +            "//goal/text()"
    2.83 +        );
    2.84 +        
    2.85 +        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
    2.86 +        NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET);
    2.87 +        
    2.88 +        for (int i = 0; i < goals.getLength(); i++) {
    2.89 +            String s = goals.item(i).getTextContent();
    2.90 +            if (s.contains("apidesign")) {
    2.91 +                assertFalse(s.matches(".*apidesign.*[0-9].*"), "No numbers: " + s);
    2.92 +            }
    2.93 +        }
    2.94 +    }
    2.95 +
    2.96 +    static String findCurrentVersion() throws XPathExpressionException, IOException, ParserConfigurationException, SAXException, XPathFactoryConfigurationException {
    2.97 +        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
    2.98 +        URL u = l.getResource("META-INF/maven/org.apidesign.html/knockout4j-archetype/pom.xml");
    2.99 +        assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path"));
   2.100 +
   2.101 +        final XPathFactory fact = XPathFactory.newInstance();
   2.102 +        fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
   2.103 +
   2.104 +        XPathExpression xp = fact.newXPath().compile("project/version/text()");
   2.105 +        
   2.106 +        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream());
   2.107 +        return xp.evaluate(dom);
   2.108 +    }
   2.109 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/VerifyArchetypeTest.java	Mon May 13 14:25:37 2013 +0200
     3.3 @@ -0,0 +1,64 @@
     3.4 +/**
     3.5 + * HTML via Java(tm) Language Bindings
     3.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details. apidesign.org
    3.16 + * designates this particular file as subject to the
    3.17 + * "Classpath" exception as provided by apidesign.org
    3.18 + * in the License file that accompanied this code.
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License
    3.21 + * along with this program. Look for COPYING file in the top folder.
    3.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    3.23 + */
    3.24 +package org.apidesign.html.archetype.test;
    3.25 +
    3.26 +import java.io.File;
    3.27 +import java.util.Properties;
    3.28 +import org.apache.maven.it.Verifier;
    3.29 +import org.testng.annotations.Test;
    3.30 +import static org.testng.Assert.*;
    3.31 +
    3.32 +/**
    3.33 + *
    3.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.35 + */
    3.36 +public class VerifyArchetypeTest {
    3.37 +    @Test public void fxBrwsrCompiles() throws Exception {
    3.38 +        final File dir = new File("target/tests/fxcompile/").getAbsoluteFile();
    3.39 +        generateFromArchetype(dir);
    3.40 +        
    3.41 +        File created = new File(dir, "o-a-test");
    3.42 +        assertTrue(created.isDirectory(), "Project created");
    3.43 +        assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
    3.44 +        
    3.45 +        Verifier v = new Verifier(created.getAbsolutePath());
    3.46 +        v.executeGoal("verify");
    3.47 +        
    3.48 +        v.verifyErrorFreeLog();
    3.49 +    }
    3.50 +
    3.51 +    private Verifier generateFromArchetype(final File dir) throws Exception {
    3.52 +        Verifier v = new Verifier(dir.getAbsolutePath());
    3.53 +        v.setAutoclean(false);
    3.54 +        v.deleteDirectory("");
    3.55 +        dir.mkdirs();
    3.56 +        Properties sysProp = v.getSystemProperties();
    3.57 +        sysProp.put("groupId", "org.apidesign.test");
    3.58 +        sysProp.put("artifactId", "o-a-test");
    3.59 +        sysProp.put("package", "org.apidesign.test.oat");
    3.60 +        sysProp.put("archetypeGroupId", "org.apidesign.html");
    3.61 +        sysProp.put("archetypeArtifactId", "knockout4j-archetype");
    3.62 +        sysProp.put("archetypeVersion", ArchetypeVersionTest.findCurrentVersion());
    3.63 +        v.executeGoal("archetype:generate");
    3.64 +        v.verifyErrorFreeLog();
    3.65 +        return v;
    3.66 +    }
    3.67 +}
     4.1 --- a/ko-archetype/pom.xml	Mon May 13 11:39:33 2013 +0200
     4.2 +++ b/ko-archetype/pom.xml	Mon May 13 14:25:37 2013 +0200
     4.3 @@ -53,36 +53,6 @@
     4.4                    <target>1.6</target>
     4.5                </configuration>
     4.6            </plugin>
     4.7 -          <plugin>
     4.8 -              <groupId>org.apache.maven.plugins</groupId>
     4.9 -              <artifactId>maven-surefire-plugin</artifactId>
    4.10 -              <configuration>
    4.11 -                  <skipTests>true</skipTests>
    4.12 -              </configuration>
    4.13 -              <executions>
    4.14 -                  <execution>
    4.15 -                      <id>test</id>
    4.16 -                      <goals>
    4.17 -                          <goal>test</goal>
    4.18 -                      </goals>
    4.19 -                      <phase>integration-test</phase>
    4.20 -                      <configuration>
    4.21 -                          <additionalClasspathElements>
    4.22 -                              <additionalClasspathElement>${project.build.directory}/knockout4j-archetype-${project.version}.jar</additionalClasspathElement>
    4.23 -                          </additionalClasspathElements>
    4.24 -                          <skipTests>false</skipTests>
    4.25 -                      </configuration>
    4.26 -                  </execution>
    4.27 -                  
    4.28 -              </executions>
    4.29 -          </plugin>
    4.30        </plugins>
    4.31    </build>
    4.32 -  <dependencies>
    4.33 -      <dependency>
    4.34 -          <groupId>org.testng</groupId>
    4.35 -          <artifactId>testng</artifactId>
    4.36 -          <scope>test</scope>
    4.37 -      </dependency>
    4.38 -  </dependencies>
    4.39  </project>
     5.1 --- a/ko-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml	Mon May 13 11:39:33 2013 +0200
     5.2 +++ b/ko-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml	Mon May 13 14:25:37 2013 +0200
     5.3 @@ -4,7 +4,7 @@
     5.4      <fileSet filtered="true" packaged="true">
     5.5        <directory>src/main/java</directory>
     5.6        <includes>
     5.7 -        <include>**/App.java</include>
     5.8 +        <include>**/*.java</include>
     5.9        </includes>
    5.10      </fileSet>
    5.11      <fileSet filtered="true" packaged="true">
    5.12 @@ -12,6 +12,7 @@
    5.13        <includes>
    5.14          <include>**/*.xhtml</include>
    5.15          <include>**/*.html</include>
    5.16 +        <include>**/*.css</include>
    5.17        </includes>
    5.18      </fileSet>
    5.19      <fileSet filtered="true" packaged="true">
    5.20 @@ -20,6 +21,12 @@
    5.21          <include>**/*Test.java</include>
    5.22        </includes>
    5.23      </fileSet>
    5.24 +    <fileSet filtered="true" packaged="false">
    5.25 +      <directory>src/main/assembly</directory>
    5.26 +      <includes>
    5.27 +        <include>**/*.xml</include>
    5.28 +      </includes>
    5.29 +    </fileSet>
    5.30      <fileSet filtered="false" packaged="false">
    5.31        <directory></directory>
    5.32        <includes>
     6.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/App.java	Mon May 13 11:39:33 2013 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,88 +0,0 @@
     6.4 -package ${package};
     6.5 -
     6.6 -import java.util.List;
     6.7 -import org.apidesign.bck2brwsr.htmlpage.api.*;
     6.8 -import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
     6.9 -import org.apidesign.bck2brwsr.htmlpage.api.Page;
    6.10 -import org.apidesign.bck2brwsr.htmlpage.api.Property;
    6.11 -import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
    6.12 -
    6.13 -/** This is the controller class for associated index.html page. The <code>Index</code>
    6.14 - * is autogenerated by parsing the index.html page. It fields represent individual
    6.15 - * elements annotated by "id" in the page.
    6.16 - */
    6.17 -@Page(xhtml="index.html", className="Index", properties={
    6.18 -    @Property(name="name", type=String.class),
    6.19 -    @Property(name="messages", type=String.class, array=true),
    6.20 -})
    6.21 -public class App {
    6.22 -    static {
    6.23 -        Index model = new Index();
    6.24 -        model.setName("World");
    6.25 -        model.applyBindings();
    6.26 -    }
    6.27 -    
    6.28 -    /** 
    6.29 -     * @param m the model of the index page creates in static initializer
    6.30 -     */
    6.31 -    @On(event = CLICK, id="hello")
    6.32 -    static void hello(Index m) {
    6.33 -        display(m.getHelloMessage(), m);
    6.34 -        m.getMessages().add(m.getHelloMessage());
    6.35 -    }
    6.36 -
    6.37 -    /** Reacts when mouse moves over the canvas.
    6.38 -     * 
    6.39 -     * @param m the model of the page
    6.40 -     * @param x property "x" extracted from the event generated by the browser
    6.41 -     * @param y property "y" from the mouse event
    6.42 -     */
    6.43 -    @On(event = MOUSE_MOVE, id="canvas")
    6.44 -    static void clearPoint(Index m, int x, int y) {
    6.45 -        GraphicsContext g = m.canvas.getContext();
    6.46 -        boolean even = (x + y) % 2 == 0;
    6.47 -        if (even) {
    6.48 -            g.setFillStyle("blue");
    6.49 -        } else {
    6.50 -            g.setFillStyle("red");
    6.51 -        }
    6.52 -        g.clearRect(0, 0, 1000, 1000);
    6.53 -        g.setFont("italic 40px Calibri");
    6.54 -        g.fillText(m.getHelloMessage(), 10, 40);
    6.55 -    }
    6.56 -
    6.57 -    /** Callback function called by the KnockOut/Java binding on elements
    6.58 -     * representing href's with individual messages being their data.
    6.59 -     * 
    6.60 -     * @param data the data associated with the element 
    6.61 -     * @param m the model of the page
    6.62 -     */
    6.63 -    @OnFunction
    6.64 -    static void display(String data, Index m) {
    6.65 -        GraphicsContext g = m.canvas.getContext();
    6.66 -        g.clearRect(0, 0, 1000, 1000);
    6.67 -        g.setFillStyle("black");
    6.68 -        g.setFont("italic 40px Calibri");
    6.69 -        g.fillText(data, 10, 40);
    6.70 -    }
    6.71 -
    6.72 -    /** Callback function.
    6.73 -     * 
    6.74 -     * @param data data associated with the actual element on the page
    6.75 -     * @param m the model of the page
    6.76 -     */
    6.77 -    @OnFunction
    6.78 -    static void remove(String data, Index m) {
    6.79 -        m.getMessages().remove(data);
    6.80 -    }
    6.81 -    
    6.82 -    @ComputedProperty
    6.83 -    static String helloMessage(String name) {
    6.84 -        return "Hello " + name + "!";
    6.85 -    }
    6.86 -    
    6.87 -    @ComputedProperty
    6.88 -    static boolean noMessages(List<String> messages) {
    6.89 -        return messages.isEmpty();
    6.90 -    }
    6.91 -}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java	Mon May 13 14:25:37 2013 +0200
     7.3 @@ -0,0 +1,177 @@
     7.4 +package ${package};
     7.5 +
     7.6 +import java.util.Arrays;
     7.7 +import java.util.List;
     7.8 +import net.java.html.json.ComputedProperty;
     7.9 +import net.java.html.json.Context;
    7.10 +import net.java.html.json.Function;
    7.11 +import net.java.html.json.Model;
    7.12 +import net.java.html.json.OnPropertyChange;
    7.13 +import net.java.html.json.OnReceive;
    7.14 +import net.java.html.json.Property;
    7.15 +
    7.16 +@Model(className="TwitterModel", properties={
    7.17 +    @Property(name="savedLists", type=Tweeters.class, array = true),
    7.18 +    @Property(name="activeTweetersName", type=String.class),
    7.19 +    @Property(name="activeTweeters", type=String.class, array = true),
    7.20 +    @Property(name="userNameToAdd", type=String.class),
    7.21 +    @Property(name="currentTweets", type=Tweet.class, array = true)
    7.22 +})
    7.23 +public class TwitterClient {
    7.24 +    @Model(className = "Tweeters", properties = {
    7.25 +        @Property(name="name", type = String.class),
    7.26 +        @Property(name="userNames", type = String.class, array = true)
    7.27 +    })
    7.28 +    static class Twttrs {
    7.29 +    }
    7.30 +    @Model(className = "Tweet", properties = {
    7.31 +        @Property(name = "from_user", type = String.class),
    7.32 +        @Property(name = "from_user_id", type = int.class),
    7.33 +        @Property(name = "profile_image_url", type = String.class),
    7.34 +        @Property(name = "text", type = String.class),
    7.35 +        @Property(name = "created_at", type = String.class),
    7.36 +    })
    7.37 +    static final class Twt {
    7.38 +        @ComputedProperty static String html(String text) {
    7.39 +            StringBuilder sb = new StringBuilder(320);
    7.40 +            for (int pos = 0;;) {
    7.41 +                int http = text.indexOf("http", pos);
    7.42 +                if (http == -1) {
    7.43 +                    sb.append(text.substring(pos));
    7.44 +                    return sb.toString();
    7.45 +                }
    7.46 +                int spc = text.indexOf(' ', http);
    7.47 +                if (spc == -1) {
    7.48 +                    spc = text.length();
    7.49 +                }
    7.50 +                sb.append(text.substring(pos, http));
    7.51 +                String url = text.substring(http, spc);
    7.52 +                sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
    7.53 +                pos = spc;
    7.54 +            }
    7.55 +        }
    7.56 +        
    7.57 +        @ComputedProperty static String userUrl(String from_user) {
    7.58 +            return "http://twitter.com/" + from_user;
    7.59 +        }
    7.60 +    }
    7.61 +    @Model(className = "TwitterQuery", properties = {
    7.62 +        @Property(array = true, name = "results", type = Twt.class)
    7.63 +    })
    7.64 +    public static final class TwttrQr {
    7.65 +    }
    7.66 +    
    7.67 +    @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
    7.68 +    static void queryTweets(TwitterModel page, TwitterQuery q) {
    7.69 +        page.getCurrentTweets().clear();
    7.70 +        page.getCurrentTweets().addAll(q.getResults());
    7.71 +    }
    7.72 +    
    7.73 +    @OnPropertyChange("activeTweetersName")
    7.74 +    static void changeTweetersList(TwitterModel model) {
    7.75 +        Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());        
    7.76 +        model.getActiveTweeters().clear();
    7.77 +        model.getActiveTweeters().addAll(people.getUserNames());
    7.78 +    }
    7.79 +    
    7.80 +    @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
    7.81 +    static void refreshTweets(TwitterModel model) {
    7.82 +        StringBuilder sb = new StringBuilder();
    7.83 +        sb.append("rpp=25&q=");
    7.84 +        String sep = "";
    7.85 +        for (String p : model.getActiveTweeters()) {
    7.86 +            sb.append(sep);
    7.87 +            sb.append("from:");
    7.88 +            sb.append(p);
    7.89 +            sep = " OR ";
    7.90 +        }
    7.91 +        model.queryTweets("http://search.twitter.com", sb.toString());
    7.92 +    }
    7.93 +    
    7.94 +    private static final Context DEFAULT = Context.findDefault(TwitterClient.class);
    7.95 +    static {
    7.96 +        final TwitterModel model = new TwitterModel(DEFAULT);
    7.97 +        final List<Tweeters> svdLst = model.getSavedLists();
    7.98 +        svdLst.add(newTweeters("API Design", "JaroslavTulach"));
    7.99 +        svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
   7.100 +        svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
   7.101 +        svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
   7.102 +        svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
   7.103 +
   7.104 +        model.setActiveTweetersName("NetBeans");
   7.105 +
   7.106 +        model.applyBindings();
   7.107 +    }
   7.108 +    
   7.109 +    @ComputedProperty
   7.110 +    static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
   7.111 +        Tweeters tw = findByName(savedLists, activeTweetersName);
   7.112 +        if (activeTweeters == null) {
   7.113 +            return false;
   7.114 +        }
   7.115 +        return !tw.getUserNames().equals(activeTweeters);
   7.116 +    }
   7.117 +    
   7.118 +    @ComputedProperty
   7.119 +    static int activeTweetersCount(List<String> activeTweeters) {
   7.120 +        return activeTweeters.size();
   7.121 +    }
   7.122 +    
   7.123 +    @ComputedProperty
   7.124 +    static boolean userNameToAddIsValid(
   7.125 +        String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
   7.126 +    ) {
   7.127 +        return userNameToAdd != null && 
   7.128 +            userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
   7.129 +            !activeTweeters.contains(userNameToAdd);
   7.130 +    }
   7.131 +    
   7.132 +    @Function
   7.133 +    static void deleteList(TwitterModel model) {
   7.134 +        final List<Tweeters> sl = model.getSavedLists();
   7.135 +        sl.remove(findByName(sl, model.getActiveTweetersName()));
   7.136 +        if (sl.isEmpty()) {
   7.137 +            final Tweeters t = new Tweeters(DEFAULT);
   7.138 +            t.setName("New");
   7.139 +            sl.add(t);
   7.140 +        }
   7.141 +        model.setActiveTweetersName(sl.get(0).getName());
   7.142 +    }
   7.143 +    
   7.144 +    @Function
   7.145 +    static void saveChanges(TwitterModel model) {
   7.146 +        Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
   7.147 +        int indx = model.getSavedLists().indexOf(t);
   7.148 +        if (indx != -1) {
   7.149 +            t.setName(model.getActiveTweetersName());
   7.150 +            t.getUserNames().clear();
   7.151 +            t.getUserNames().addAll(model.getActiveTweeters());
   7.152 +        }
   7.153 +    }
   7.154 +    
   7.155 +    @Function
   7.156 +    static void addUser(TwitterModel model) {
   7.157 +        String n = model.getUserNameToAdd();
   7.158 +        model.getActiveTweeters().add(n);
   7.159 +    }
   7.160 +    @Function
   7.161 +    static void removeUser(String data, TwitterModel model) {
   7.162 +        model.getActiveTweeters().remove(data);
   7.163 +    }
   7.164 +    
   7.165 +    private static Tweeters findByName(List<Tweeters> list, String name) {
   7.166 +        for (Tweeters l : list) {
   7.167 +            if (l.getName() != null && l.getName().equals(name)) {
   7.168 +                return l;
   7.169 +            }
   7.170 +        }
   7.171 +        return list.isEmpty() ? new Tweeters(DEFAULT) : list.get(0);
   7.172 +    }
   7.173 +    
   7.174 +    private static Tweeters newTweeters(String listName, String... userNames) {
   7.175 +        Tweeters t = new Tweeters(DEFAULT);
   7.176 +        t.setName(listName);
   7.177 +        t.getUserNames().addAll(Arrays.asList(userNames));
   7.178 +        return t;
   7.179 +    }
   7.180 +}
     8.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/resources/index.html	Mon May 13 11:39:33 2013 +0200
     8.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/resources/index.html	Mon May 13 14:25:37 2013 +0200
     8.3 @@ -1,31 +1,105 @@
     8.4  <?xml version="1.0" encoding="UTF-8"?>
     8.5 +<!--
     8.6 +
     8.7 +    The MIT License (MIT)
     8.8 +
     8.9 +    Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    8.10 +
    8.11 +    Permission is hereby granted, free of charge, to any person obtaining a copy
    8.12 +    of this software and associated documentation files (the "Software"), to deal
    8.13 +    in the Software without restriction, including without limitation the rights
    8.14 +    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    8.15 +    copies of the Software, and to permit persons to whom the Software is
    8.16 +    furnished to do so, subject to the following conditions:
    8.17 +
    8.18 +    The above copyright notice and this permission notice shall be included in
    8.19 +    all copies or substantial portions of the Software.
    8.20 +
    8.21 +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    8.22 +    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    8.23 +    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    8.24 +    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    8.25 +    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    8.26 +    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    8.27 +    THE SOFTWARE.
    8.28 +
    8.29 +-->
    8.30 +
    8.31 +<!--
    8.32 +    Copied from knockout.js Twitter example:
    8.33 +    http://knockoutjs.com/examples/twitter.html
    8.34 +-->
    8.35 +
    8.36  <!DOCTYPE html>
    8.37  <html xmlns="http://www.w3.org/1999/xhtml">
    8.38      <head>
    8.39 -        <title>Bck2Brwsr's Hello World</title>
    8.40 +        <title>Bck2Brwsr's Twitter</title>
    8.41      </head>
    8.42      <body>
    8.43 -        <h1 data-bind="text: helloMessage">Loading Bck2Brwsr's Hello World...</h1>
    8.44 -        Your name: <input id='input' data-bind="value: name, valueUpdate: 'afterkeydown'"></input>
    8.45 -        <button id="hello">Say Hello!</button>
    8.46 +        <link href='twitterExample.css' rel='Stylesheet' ></link>
    8.47 +        
    8.48 +        <style type='text/css'>
    8.49 +           .liveExample select { height: 1.7em; }
    8.50 +           .liveExample button { height: 2em; }
    8.51 +        </style>
    8.52 +        
    8.53 +        
    8.54 +        <h2>Bck2Brwsr's Twitter</h2>
    8.55 +        
    8.56          <p>
    8.57 -            <canvas id="canvas" width="300" height="50">
    8.58 -            </canvas>
    8.59 +        This code based on original <a href="http://knockoutjs.com/examples/twitter.html">knockout.js Twitter example</a> and
    8.60 +        uses almost unmodified HTML code. It just changes the model. It 
    8.61 +        is written in Java language and it is executed using <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a>
    8.62 +        virtual machine. The Java source code has about 190 lines and is available 
    8.63 +        <a href="http://source.apidesign.org/hg/bck2brwsr/file/7fc6b7e9c982/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java">here</a>
    8.64 +        - in fact it may even be more dense than the original JavaScript model.
    8.65          </p>
    8.66          
    8.67 +        <div class='liveExample'>
    8.68 +            <div class='configuration'>
    8.69 +                <div class='listChooser'>
    8.70 +                    <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
    8.71 +                    <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button> 
    8.72 +                    <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
    8.73 +                </div>
    8.74 +
    8.75 +                <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
    8.76 +                <div class='currentUsers' >
    8.77 +                    <ul data-bind='foreach: activeTweeters'>
    8.78 +                        <li>
    8.79 +                            <button data-bind='click: $root.removeUser'>Remove</button>
    8.80 +                            <div data-bind='text: $data'> </div>
    8.81 +                        </li>
    8.82 +                    </ul>
    8.83 +                </div>
    8.84 +
    8.85 +                <form data-bind='submit: addUser'>
    8.86 +                    <label>Add user:</label>
    8.87 +                    <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
    8.88 +                    <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
    8.89 +                </form>
    8.90 +            </div>
    8.91 +            <div class='tweets'>
    8.92 +                <div class='loadingIndicator'>Loading...</div>
    8.93 +                <table data-bind='foreach: currentTweets' width='100%'>
    8.94 +                    <tr>
    8.95 +                        <td><img data-bind='attr: { src: profile_image_url }' /></td>
    8.96 +                        <td>
    8.97 +                            <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
    8.98 +                            <span data-bind='html: html'> </span>
    8.99 +                            <div class='tweetInfo' data-bind='text: created_at'> </div>
   8.100 +                        </td>
   8.101 +                    </tr>
   8.102 +                </table>
   8.103 +            </div>
   8.104 +        </div>
   8.105          
   8.106 -        <div data-bind="if: noMessages">No message displayed yet.</div>
   8.107 -        <ul data-bind="foreach: messages">
   8.108 -            <li>
   8.109 -                <a href="#" data-bind="text: $data, click: $root.display"></a>
   8.110 -                (<a href="#" data-bind="click: $root.remove">delete</a>)
   8.111 -            </li>
   8.112 -        </ul>
   8.113 -      
   8.114          <script src="bck2brwsr.js"></script>
   8.115          <script type="text/javascript">
   8.116 -            var vm = bck2brwsr('${artifactId}-${version}.jar');
   8.117 -            vm.loadClass('${package}.App');
   8.118 +            var vm = bck2brwsr('demo-twitter-1.0-SNAPSHOT.jar');
   8.119 +            vm.loadClass('org.apidesign.html.demo.twitter.TwitterClient');
   8.120          </script>
   8.121 +
   8.122 +
   8.123      </body>
   8.124  </html>
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css	Mon May 13 14:25:37 2013 +0200
     9.3 @@ -0,0 +1,58 @@
     9.4 +/**
     9.5 + * The MIT License (MIT)
     9.6 + *
     9.7 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     9.8 + *
     9.9 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    9.10 + * of this software and associated documentation files (the "Software"), to deal
    9.11 + * in the Software without restriction, including without limitation the rights
    9.12 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    9.13 + * copies of the Software, and to permit persons to whom the Software is
    9.14 + * furnished to do so, subject to the following conditions:
    9.15 + *
    9.16 + * The above copyright notice and this permission notice shall be included in
    9.17 + * all copies or substantial portions of the Software.
    9.18 + *
    9.19 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    9.20 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    9.21 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    9.22 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    9.23 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    9.24 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    9.25 + * THE SOFTWARE.
    9.26 + */
    9.27 +
    9.28 +
    9.29 +
    9.30 +/*
    9.31 +    Copied from knockout.js Twitter example:
    9.32 +    http://knockoutjs.com/examples/twitter.html
    9.33 +*/
    9.34 +
    9.35 +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
    9.36 +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
    9.37 +.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
    9.38 +.tweets table { border-width: 0;}
    9.39 +.tweets tr { vertical-align: top; }
    9.40 +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
    9.41 +.tweets img { width: 4em; }
    9.42 +.tweetInfo { color: Gray; font-size: 0.9em; }
    9.43 +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
    9.44 +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
    9.45 +
    9.46 +.listChooser select, .listChooser button { vertical-align:top; }
    9.47 +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
    9.48 +.listChooser button { width: 19%; height:1.68em; float:right; }
    9.49 +
    9.50 +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
    9.51 +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
    9.52 +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
    9.53 +.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; }
    9.54 +.currentUsers li div { padding: 0.6em; }
    9.55 +.currentUsers li:hover { background-color: #EEC; }
    9.56 +
    9.57 +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
    9.58 +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
    9.59 +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
    9.60 +
    9.61 +.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; display: none; }
    10.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/AppTest.java	Mon May 13 11:39:33 2013 +0200
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,26 +0,0 @@
    10.4 -package ${package};
    10.5 -
    10.6 -import static org.testng.Assert.*;
    10.7 -import org.testng.annotations.BeforeMethod;
    10.8 -import org.testng.annotations.Test;
    10.9 -
   10.10 -/** Demonstrating POJO testing of HTML page model. Runs in good old HotSpot
   10.11 - * as it does not reference any HTML elements or browser functionality. Just
   10.12 - * operates on the page model.
   10.13 - *
   10.14 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   10.15 - */
   10.16 -public class AppTest {
   10.17 -    private Index model;
   10.18 -    
   10.19 -
   10.20 -    @BeforeMethod
   10.21 -    public void initModel() {
   10.22 -        model = new Index().applyBindings();
   10.23 -    }
   10.24 -
   10.25 -    @Test public void testHelloMessage() {
   10.26 -        model.setName("Joe");
   10.27 -        assertEquals(model.getHelloMessage(), "Hello Joe!", "Cleared after pressing +");
   10.28 -    }
   10.29 -}
    11.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/InconsistencyTest.java	Mon May 13 11:39:33 2013 +0200
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,40 +0,0 @@
    11.4 -package ${package};
    11.5 -
    11.6 -import org.apidesign.bck2brwsr.vmtest.Compare;
    11.7 -import org.apidesign.bck2brwsr.vmtest.VMTest;
    11.8 -import org.testng.annotations.Factory;
    11.9 -
   11.10 -/** Bck2brwsr cares about compatibility with real Java. Whatever API is
   11.11 - * supported by bck2brwsr, it needs to behave the same way as when running
   11.12 - * in HotSpot VM. 
   11.13 - * <p>
   11.14 - * There can be bugs, however. To help us fix them, we kindly ask you to 
   11.15 - * write an "inconsistency" test. A test that compares behavior of the API
   11.16 - * between real VM and bck2brwsr VM. This class is skeleton of such test.
   11.17 - *
   11.18 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   11.19 - */
   11.20 -public class InconsistencyTest {
   11.21 -    /** A method to demonstrate inconsistency between bck2brwsr and HotSpot.
   11.22 -     * Make calls to an API that behaves strangely, return some result at
   11.23 -     * the end. No need to use any <code>assert</code>.
   11.24 -     * 
   11.25 -     * @return value to compare between HotSpot and bck2brwsr
   11.26 -     */
   11.27 -    @Compare
   11.28 -    public int checkStringHashCode() throws Exception {
   11.29 -        return "Is string hashCode the same?".hashCode();
   11.30 -    }
   11.31 -
   11.32 -    /** Factory method that creates a three tests for each method annotated with
   11.33 -     * {@link org.apidesign.bck2brwsr.vmtest.Compare}. One executes the code in
   11.34 -     * HotSpot, one in Rhino and the last one compares the results.
   11.35 -     * 
   11.36 -     * @see org.apidesign.bck2brwsr.vmtest.VMTest
   11.37 -     */
   11.38 -    @Factory
   11.39 -    public static Object[] create() {
   11.40 -        return VMTest.create(InconsistencyTest.class);
   11.41 -    }
   11.42 -    
   11.43 -}
    12.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java	Mon May 13 11:39:33 2013 +0200
    12.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java	Mon May 13 14:25:37 2013 +0200
    12.3 @@ -1,6 +1,5 @@
    12.4  package ${package};
    12.5  
    12.6 -import org.apidesign.bck2brwsr.htmlpage.api.OnEvent;
    12.7  import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
    12.8  import org.apidesign.bck2brwsr.vmtest.HtmlFragment;
    12.9  import org.apidesign.bck2brwsr.vmtest.VMTest;
   12.10 @@ -11,8 +10,6 @@
   12.11   * and that is it. If your code references elements on the HTML page,
   12.12   * you can pass in an {@link org.apidesign.bck2brwsr.vmtest.HtmlFragment} which
   12.13   * will be made available on the page before your test starts.
   12.14 - *
   12.15 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   12.16   */
   12.17  public class IntegrationTest {
   12.18      
   12.19 @@ -20,22 +17,10 @@
   12.20       * Assert, as TestNG is not compiled with target 1.6 yet).
   12.21       */
   12.22      @HtmlFragment(
   12.23 -        "<h1 data-bind=\"text: helloMessage\">Loading Bck2Brwsr's Hello World...</h1>\n" +
   12.24 -        "Your name: <input id='input' data-bind=\"value: name, valueUpdate: 'afterkeydown'\"></input>\n" +
   12.25 -        "<button id=\"hello\">Say Hello!</button>\n" +
   12.26 -        "<p>\n" +
   12.27 -        "    <canvas id=\"canvas\" width=\"300\" height=\"50\"></canvas>\n" +
   12.28 -        "</p>\n"
   12.29 +        "<h1>Put this snippet on the HTML page</h1>\n"
   12.30      )
   12.31      @BrwsrTest
   12.32 -    public void modifyValueAssertChangeInModel() {
   12.33 -        Index m = new Index();
   12.34 -        m.setName("Joe Hacker");
   12.35 -        m.applyBindings();
   12.36 -        assert "Joe Hacker".equals(m.input.getValue()) : "Value is really Joe Hacker: " + m.input.getValue();
   12.37 -        m.input.setValue("Happy Joe");
   12.38 -        m.triggerEvent(m.input, OnEvent.CHANGE);
   12.39 -        assert "Happy Joe".equals(m.getName()) : "Name property updated to Happy Joe: " + m.getName();
   12.40 +    public void runThisTestInABrowser() {
   12.41      }
   12.42  
   12.43      @Factory
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java	Mon May 13 14:25:37 2013 +0200
    13.3 @@ -0,0 +1,49 @@
    13.4 +package ${package};
    13.5 +
    13.6 +import java.util.List;
    13.7 +import net.java.html.json.Context;
    13.8 +import static org.testng.Assert.*;
    13.9 +import org.testng.annotations.BeforeMethod;
   13.10 +import org.testng.annotations.Test;
   13.11 +
   13.12 +/** We can unit test the TwitterModel smoothly.
   13.13 + */
   13.14 +public class TwitterClientTest {
   13.15 +    private TwitterModel model;
   13.16 +    
   13.17 +
   13.18 +    @BeforeMethod
   13.19 +    public void initModel() {
   13.20 +        model = new TwitterModel(Context.EMPTY);
   13.21 +    }
   13.22 +
   13.23 +    @Test public void testIsValidToAdd() {
   13.24 +        model.setUserNameToAdd("Joe");
   13.25 +        Tweeters t = new Tweeters(Context.EMPTY);
   13.26 +        t.setName("test");
   13.27 +        model.getSavedLists().add(t);
   13.28 +        model.setActiveTweetersName("test");
   13.29 +        
   13.30 +        assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
   13.31 +        TwitterClient.addUser(model);
   13.32 +        assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
   13.33 +        assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
   13.34 +        
   13.35 +        List<String> mod = model.getActiveTweeters();
   13.36 +        assertTrue(model.isHasUnsavedChanges(), "We have modifications");
   13.37 +        assertEquals(mod.size(), 1, "One element in the list");
   13.38 +        assertEquals(mod.get(0), "Joe", "Its name is Joe");
   13.39 +        
   13.40 +        assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
   13.41 +        
   13.42 +        TwitterClient.saveChanges(model);
   13.43 +        assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
   13.44 +        
   13.45 +        assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
   13.46 +    }
   13.47 +    
   13.48 +    @Test public void httpAtTheEnd() {
   13.49 +        String res = TwitterClient.Twt.html("Ahoj http://kuk");
   13.50 +        assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
   13.51 +    }
   13.52 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java	Mon May 13 14:25:37 2013 +0200
    14.3 @@ -0,0 +1,74 @@
    14.4 +package ${package};
    14.5 +
    14.6 +import net.java.html.json.Context;
    14.7 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
    14.8 +import org.apidesign.bck2brwsr.vmtest.Http;
    14.9 +import org.apidesign.bck2brwsr.vmtest.VMTest;
   14.10 +import org.testng.annotations.Factory;
   14.11 +
   14.12 +public class TwitterProtocolTest {
   14.13 +    private TwitterModel page;
   14.14 +    @Http(@Http.Resource(
   14.15 +        path = "/search.json",
   14.16 +        mimeType = "application/json",
   14.17 +        parameters = {"callback"},
   14.18 +        content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
   14.19 +        + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
   14.20 +        + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
   14.21 +        + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
   14.22 +        + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
   14.23 +        + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
   14.24 +        + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
   14.25 +        + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.26 +        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.27 +        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.28 +        + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
   14.29 +        + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
   14.30 +        + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
   14.31 +        + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
   14.32 +        + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
   14.33 +        + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
   14.34 +        + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
   14.35 +        + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.36 +        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.37 +        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.38 +        + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
   14.39 +        + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
   14.40 +        + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
   14.41 +        + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
   14.42 +        + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
   14.43 +        + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.44 +        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.45 +        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.46 +        + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
   14.47 +        + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
   14.48 +        + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
   14.49 +        + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
   14.50 +        + "{\"result_type\":\"recent\"},\"profile_image_url\":"
   14.51 +        + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.52 +        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.53 +        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.54 +        + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
   14.55 +        + "\"since_id\":0,\"since_id_str\":\"0\"})"
   14.56 +    ))
   14.57 +    @BrwsrTest public void readFromTwttr() throws InterruptedException {
   14.58 +        if (page == null) {
   14.59 +            page = new TwitterModel(Context.findDefault(TwitterProtocolTest.class));
   14.60 +            page.applyBindings();
   14.61 +            page.queryTweets("", "q=xyz");
   14.62 +        }
   14.63 +
   14.64 +        if (page.getCurrentTweets().isEmpty()) {
   14.65 +            throw new InterruptedException();
   14.66 +        }
   14.67 +
   14.68 +        assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
   14.69 +        
   14.70 +        String firstDate = page.getCurrentTweets().get(0).getCreated_at();
   14.71 +        assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
   14.72 +    }
   14.73 +    
   14.74 +    @Factory public static Object[] create() {
   14.75 +        return VMTest.create(TwitterProtocolTest.class);
   14.76 +    }
   14.77 +}
    15.1 --- a/ko-archetype/src/test/java/org/apidesign/html/archetype/ArchetypeVersionTest.java	Mon May 13 11:39:33 2013 +0200
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,98 +0,0 @@
    15.4 -/**
    15.5 - * HTML via Java(tm) Language Bindings
    15.6 - * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    15.7 - *
    15.8 - * This program is free software: you can redistribute it and/or modify
    15.9 - * it under the terms of the GNU General Public License as published by
   15.10 - * the Free Software Foundation, version 2 of the License.
   15.11 - *
   15.12 - * This program is distributed in the hope that it will be useful,
   15.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15.15 - * GNU General Public License for more details. apidesign.org
   15.16 - * designates this particular file as subject to the
   15.17 - * "Classpath" exception as provided by apidesign.org
   15.18 - * in the License file that accompanied this code.
   15.19 - *
   15.20 - * You should have received a copy of the GNU General Public License
   15.21 - * along with this program. Look for COPYING file in the top folder.
   15.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   15.23 - */
   15.24 -package org.apidesign.html.archetype;
   15.25 -
   15.26 -import java.net.URL;
   15.27 -import javax.xml.XMLConstants;
   15.28 -import javax.xml.parsers.DocumentBuilderFactory;
   15.29 -import javax.xml.xpath.XPathConstants;
   15.30 -import javax.xml.xpath.XPathExpression;
   15.31 -import javax.xml.xpath.XPathFactory;
   15.32 -import org.testng.annotations.Test;
   15.33 -import static org.testng.Assert.*;
   15.34 -import org.testng.annotations.BeforeClass;
   15.35 -import org.w3c.dom.Document;
   15.36 -import org.w3c.dom.NodeList;
   15.37 -
   15.38 -/**
   15.39 - *
   15.40 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   15.41 - */
   15.42 -public class ArchetypeVersionTest {
   15.43 -    private String version;
   15.44 -    
   15.45 -    public ArchetypeVersionTest() {
   15.46 -    }
   15.47 -    
   15.48 -    @BeforeClass public void readCurrentVersion() throws Exception {
   15.49 -        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
   15.50 -        URL u = l.getResource("META-INF/maven/org.apidesign.html/knockout4j-archetype/pom.xml");
   15.51 -        assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path"));
   15.52 -
   15.53 -        final XPathFactory fact = XPathFactory.newInstance();
   15.54 -        fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
   15.55 -
   15.56 -        XPathExpression xp = fact.newXPath().compile("project/version/text()");
   15.57 -        
   15.58 -        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream());
   15.59 -        version = xp.evaluate(dom);
   15.60 -
   15.61 -        assertFalse(version.isEmpty(), "There should be some version string");
   15.62 -    }
   15.63 -    
   15.64 -
   15.65 -    @Test public void testComparePomDepsVersions() throws Exception {
   15.66 -        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
   15.67 -        URL r = l.getResource("archetype-resources/pom.xml");
   15.68 -        assertNotNull(r, "Archetype pom found");
   15.69 -        
   15.70 -        final XPathFactory fact = XPathFactory.newInstance();
   15.71 -        XPathExpression xp2 = fact.newXPath().compile(
   15.72 -            "//properties/net.java.html.version/text()"
   15.73 -        );
   15.74 -        
   15.75 -        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
   15.76 -        String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
   15.77 -
   15.78 -        assertEquals(arch, version, "net.java.html.json dependency needs to be on latest version");
   15.79 -    }
   15.80 -    
   15.81 -    @Test public void testNbActions() throws Exception {
   15.82 -        final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
   15.83 -        URL r = l.getResource("archetype-resources/nbactions.xml");
   15.84 -        assertNotNull(r, "Archetype nb file found");
   15.85 -        
   15.86 -        final XPathFactory fact = XPathFactory.newInstance();
   15.87 -        XPathExpression xp2 = fact.newXPath().compile(
   15.88 -            "//goal/text()"
   15.89 -        );
   15.90 -        
   15.91 -        Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
   15.92 -        NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET);
   15.93 -        
   15.94 -        for (int i = 0; i < goals.getLength(); i++) {
   15.95 -            String s = goals.item(i).getTextContent();
   15.96 -            if (s.contains("apidesign")) {
   15.97 -                assertFalse(s.matches(".*apidesign.*[0-9].*"), "No numbers: " + s);
   15.98 -            }
   15.99 -        }
  15.100 -    }
  15.101 -}