View Javadoc

1   /*
2    * Derivative Work
3    * Copyright 2010 SOFTEC sa. All rights reserved.
4    *
5    * Original Work
6    * Copyright 2010 Justin Searls
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *      http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package searls.jasmine.runner;
22  
23  import org.antlr.stringtemplate.StringTemplate;
24  import org.antlr.stringtemplate.language.DefaultTemplateLexer;
25  import org.apache.maven.artifact.Artifact;
26  import searls.jasmine.io.FileUtilsWrapper;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.net.MalformedURLException;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.List;
34  
35  public class SpecRunnerHtmlGenerator {
36  	
37  	private static final String CSS_TYPE = "css";
38  	private static final String CSS_DEPENDENCIES_TEMPLATE_ATTR_NAME = "cssDependencies";	
39  	private static final String JAVASCRIPT_TYPE = "js";
40  	private static final String JAVASCRIPT_DEPENDENCIES_TEMPLATE_ATTR_NAME = "javascriptDependencies";	
41  	private static final String SOURCES_TEMPLATE_ATTR_NAME = "sources";
42  	private static final String REPORTER_ATTR_NAME = "reporter";
43  	private static final String RUNNER_HTML_TEMPLATE =
44          "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n" +
45  		"<html>\n" +
46  		"<head><title>Jasmine Test Runner</title>\n" +
47  		"$"+CSS_DEPENDENCIES_TEMPLATE_ATTR_NAME+"$\n" +
48  		"$"+JAVASCRIPT_DEPENDENCIES_TEMPLATE_ATTR_NAME+"$\n" +
49  		"$"+SOURCES_TEMPLATE_ATTR_NAME+"$\n" +
50  		"</head>\n" +
51  		"<body>\n"+
52          "<script type=\"text/javascript\">var reporter = new jasmine.$"+REPORTER_ATTR_NAME+"$(); jasmine.getEnv().addReporter(reporter); jasmine.getEnv().execute();</script>" +
53          "\n</body>\n" +
54  		"</html>";
55  	
56  	public enum ReporterType { TrivialReporter, JsApiReporter };
57  
58  	private FileUtilsWrapper fileUtilsWrapper = new FileUtilsWrapper();
59  	
60  	private final File sourceDir;
61  	private final File specDir;
62      private final File libDir;
63      private final File baseDir;
64  	private List<String> sourcesToLoadFirst;
65  	private List<File> fileNamesAlreadyWrittenAsScriptTags = new ArrayList<File>();
66  
67  	public SpecRunnerHtmlGenerator(List<String> sourcesToLoadFirst, File sourceDir, File specDir, File libDir, File baseDir) {
68  		this.sourcesToLoadFirst = sourcesToLoadFirst;
69  		this.sourceDir = sourceDir;
70  		this.specDir = specDir;
71          this.libDir = libDir;
72          this.baseDir = baseDir;
73  	}
74  
75  	public String generate(List<Artifact> dependencies, ReporterType reporterType) {
76  		try {
77  			StringTemplate template = new StringTemplate(RUNNER_HTML_TEMPLATE,DefaultTemplateLexer.class);
78  			
79  			includeJavaScriptAndCssDependencies(dependencies, template);
80  			setJavaScriptSourcesAttribute(template);			
81  			template.setAttribute(REPORTER_ATTR_NAME, reporterType.name());
82  			
83  			return template.toString();
84  		} catch (IOException e) {
85  			throw new RuntimeException("Failed to load file names for dependencies or scripts",e);
86  		}
87  	}
88  
89  	private void includeJavaScriptAndCssDependencies(
90  			List<Artifact> dependencies, StringTemplate template)
91  			throws IOException {
92  		StringBuilder javaScriptDependencies = new StringBuilder();
93  		StringBuilder cssDependencies = new StringBuilder();
94  		for(Artifact dep : dependencies) {
95  			if(JAVASCRIPT_TYPE.equals(dep.getType())) {
96  				javaScriptDependencies.append("<script type=\"text/javascript\">").append(fileUtilsWrapper.readFileToString(dep.getFile())).append("</script>\n");
97  			} else if(CSS_TYPE.equals(dep.getType())) {
98  				cssDependencies.append("<style type=\"text/css\">\n").append(fileUtilsWrapper.readFileToString(dep.getFile())).append("\n</style>\n");
99  			}
100 		}
101 		template.setAttribute(JAVASCRIPT_DEPENDENCIES_TEMPLATE_ATTR_NAME, javaScriptDependencies.toString());
102 		template.setAttribute(CSS_DEPENDENCIES_TEMPLATE_ATTR_NAME, cssDependencies.toString());
103 	}
104 	
105 	private void setJavaScriptSourcesAttribute(StringTemplate template)
106 			throws IOException {
107 		StringBuilder scriptTags = new StringBuilder();
108 		appendScriptTagsForFiles(scriptTags, expandSourcesToLoadFirst());
109         if( sourceDir != null && sourceDir.exists() ) {
110     		appendScriptTagsForFiles(scriptTags, filesForScriptsInDirectory(sourceDir));
111         }
112 		appendScriptTagsForFiles(scriptTags, filesForScriptsInDirectory(specDir));
113 		template.setAttribute(SOURCES_TEMPLATE_ATTR_NAME,scriptTags.toString());
114 	}
115 
116 	private List<File> expandSourcesToLoadFirst() {
117 		List<File> files = new ArrayList<File>();
118 		if(sourcesToLoadFirst != null) {
119 			for(String sourceToLoadFirst : sourcesToLoadFirst) {
120                 File file = new File(sourceDir,sourceToLoadFirst);
121                 if( !file.exists() ) {
122                     file = new File(libDir,sourceToLoadFirst);
123                 }
124                 files.add(file);
125 			}
126 		}
127 		return files;
128 	}
129 
130 	private List<File> filesForScriptsInDirectory(File directory) throws IOException {
131 		List<File> files = new ArrayList<File>();
132 		if(directory != null) {
133 			fileUtilsWrapper.forceMkdir(directory);
134 			files = new ArrayList<File>(fileUtilsWrapper.listFiles(directory, new String[] {"js"}, true));
135 			Collections.sort(files); 
136 		} 
137 		return files;
138 	}
139 
140 	private void appendScriptTagsForFiles(StringBuilder sb, List<File> sourceFiles) throws MalformedURLException {
141 		for (File sourceFile : sourceFiles) {
142 			if(!fileNamesAlreadyWrittenAsScriptTags.contains(sourceFile)) {
143 				sb.append("<script type=\"text/javascript\" charset=\"utf-8\" src=\"").append(findRelativePath(baseDir,sourceFile)).append("\"></script>\n");
144 				fileNamesAlreadyWrittenAsScriptTags.add(sourceFile);
145 			}
146 		}
147 	}
148 
149     /**
150      * Build a relative file to the given base path.
151      *
152      * @param base - the file used as the base
153      * @param file - the file to compute relative to the base path
154      * @return A relative path from base to file
155      */
156     public static String findRelativePath(File base, File file) throws MalformedURLException
157     {
158         String a = ( base == null ) ? ":" : base.toURI().getPath();
159         String b = file.getParentFile().toURI().getPath();
160         String[] basePaths = a.split("/");
161         String[] otherPaths = b.split("/");
162         int n = 0;
163         for (; n < basePaths.length && n < otherPaths.length; n++) {
164             if (basePaths[n].equals(otherPaths[n]) == false) {
165                 break;
166             }
167         }
168         if( n == 0 ) {
169             return file.toURI().toURL().toString();
170         }
171 
172         StringBuffer tmp = new StringBuffer();
173         for (int m = n; m < basePaths.length; m++) {
174             tmp.append("../");
175         }
176         for (int m = n; m < otherPaths.length; m++) {
177             tmp.append(otherPaths[m]).append("/");
178         }
179         tmp.append(file.getName());
180 
181         return tmp.toString();
182     }
183 }