1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import os 6import unittest 7import StringIO 8 9from py_vulcanize import fake_fs 10from py_vulcanize import generate 11from py_vulcanize import html_generation_controller 12from py_vulcanize import html_module 13from py_vulcanize import parse_html_deps 14from py_vulcanize import project as project_module 15from py_vulcanize import resource 16from py_vulcanize import resource_loader as resource_loader 17 18 19class ResourceWithFakeContents(resource.Resource): 20 21 def __init__(self, toplevel_dir, absolute_path, fake_contents): 22 """A resource with explicitly provided contents. 23 24 If the resource does not exist, then pass fake_contents=None. This will 25 cause accessing the resource contents to raise an exception mimicking the 26 behavior of regular resources.""" 27 super(ResourceWithFakeContents, self).__init__(toplevel_dir, absolute_path) 28 self._fake_contents = fake_contents 29 30 @property 31 def contents(self): 32 if self._fake_contents is None: 33 raise Exception('File not found') 34 return self._fake_contents 35 36 37class FakeLoader(object): 38 39 def __init__(self, source_paths, initial_filenames_and_contents=None): 40 self._source_paths = source_paths 41 self._file_contents = {} 42 if initial_filenames_and_contents: 43 for k, v in initial_filenames_and_contents.iteritems(): 44 self._file_contents[k] = v 45 46 def FindResourceGivenAbsolutePath(self, absolute_path): 47 candidate_paths = [] 48 for source_path in self._source_paths: 49 if absolute_path.startswith(source_path): 50 candidate_paths.append(source_path) 51 if len(candidate_paths) == 0: 52 return None 53 54 # Sort by length. Longest match wins. 55 candidate_paths.sort(lambda x, y: len(x) - len(y)) 56 longest_candidate = candidate_paths[-1] 57 58 return ResourceWithFakeContents( 59 longest_candidate, absolute_path, 60 self._file_contents.get(absolute_path, None)) 61 62 def FindResourceGivenRelativePath(self, relative_path): 63 absolute_path = None 64 for script_path in self._source_paths: 65 absolute_path = os.path.join(script_path, relative_path) 66 if absolute_path in self._file_contents: 67 return ResourceWithFakeContents(script_path, absolute_path, 68 self._file_contents[absolute_path]) 69 return None 70 71 72class ParseTests(unittest.TestCase): 73 74 def testValidExternalScriptReferenceToRawScript(self): 75 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 76 <script src="../foo.js"> 77 """) 78 79 file_contents = {} 80 file_contents[os.path.normpath('/tmp/a/foo.js')] = """ 81'i am just some raw script'; 82""" 83 84 metadata = html_module.Parse( 85 FakeLoader([os.path.normpath('/tmp')], file_contents), 86 'a.b.start', 87 '/tmp/a/b/', 88 is_component=False, 89 parser_results=parse_results) 90 self.assertEquals([], metadata.dependent_module_names) 91 self.assertEquals( 92 ['a/foo.js'], metadata.dependent_raw_script_relative_paths) 93 94 def testExternalScriptReferenceToModuleOutsideScriptPath(self): 95 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 96 <script src="/foo.js"> 97 """) 98 99 file_contents = {} 100 file_contents[os.path.normpath('/foo.js')] = '' 101 102 def DoIt(): 103 html_module.Parse(FakeLoader([os.path.normpath('/tmp')], file_contents), 104 'a.b.start', 105 '/tmp/a/b/', 106 is_component=False, 107 parser_results=parse_results) 108 self.assertRaises(Exception, DoIt) 109 110 def testExternalScriptReferenceToFileThatDoesntExist(self): 111 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 112 <script src="/foo.js"> 113 """) 114 115 file_contents = {} 116 117 def DoIt(): 118 html_module.Parse(FakeLoader([os.path.normpath('/tmp')], file_contents), 119 'a.b.start', 120 '/tmp/a/b/', 121 is_component=False, 122 parser_results=parse_results) 123 self.assertRaises(Exception, DoIt) 124 125 def testValidImportOfModule(self): 126 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 127 <link rel="import" href="../foo.html"> 128 """) 129 130 file_contents = {} 131 file_contents[os.path.normpath('/tmp/a/foo.html')] = """ 132""" 133 134 metadata = html_module.Parse( 135 FakeLoader([os.path.normpath('/tmp')], file_contents), 136 'a.b.start', 137 '/tmp/a/b/', 138 is_component=False, 139 parser_results=parse_results) 140 self.assertEquals(['a.foo'], metadata.dependent_module_names) 141 142 def testStyleSheetImport(self): 143 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 144 <link rel="stylesheet" href="../foo.css"> 145 """) 146 147 file_contents = {} 148 file_contents[os.path.normpath('/tmp/a/foo.css')] = """ 149""" 150 metadata = html_module.Parse( 151 FakeLoader([os.path.normpath('/tmp')], file_contents), 152 'a.b.start', 153 '/tmp/a/b/', 154 is_component=False, 155 parser_results=parse_results) 156 self.assertEquals([], metadata.dependent_module_names) 157 self.assertEquals(['a.foo'], metadata.style_sheet_names) 158 159 def testUsingAbsoluteHref(self): 160 parse_results = parse_html_deps.HTMLModuleParserResults("""<!DOCTYPE html> 161 <script src="/foo.js"> 162 """) 163 164 file_contents = {} 165 file_contents[os.path.normpath('/src/foo.js')] = '' 166 167 metadata = html_module.Parse( 168 FakeLoader([os.path.normpath("/tmp"), os.path.normpath("/src")], 169 file_contents), 170 "a.b.start", 171 "/tmp/a/b/", 172 is_component=False, 173 parser_results=parse_results) 174 self.assertEquals(['foo.js'], metadata.dependent_raw_script_relative_paths) 175 176 177class HTMLModuleTests(unittest.TestCase): 178 179 def testBasicModuleGeneration(self): 180 file_contents = {} 181 file_contents[os.path.normpath('/tmp/a/b/start.html')] = """ 182<!DOCTYPE html> 183<link rel="import" href="/widget.html"> 184<link rel="stylesheet" href="../common.css"> 185<script src="/raw_script.js"></script> 186<script src="/excluded_script.js"></script> 187<dom-module id="start"> 188 <template> 189 </template> 190 <script> 191 'use strict'; 192 console.log('inline script for start.html got written'); 193 </script> 194</dom-module> 195""" 196 file_contents[os.path.normpath('/py_vulcanize/py_vulcanize.html')] = """<!DOCTYPE html> 197""" 198 file_contents[os.path.normpath('/components/widget.html')] = """ 199<!DOCTYPE html> 200<link rel="import" href="/py_vulcanize.html"> 201<widget name="widget.html"></widget> 202<script> 203'use strict'; 204console.log('inline script for widget.html'); 205</script> 206""" 207 file_contents[os.path.normpath('/tmp/a/common.css')] = """ 208/* /tmp/a/common.css was written */ 209""" 210 file_contents[os.path.normpath('/raw/raw_script.js')] = """ 211console.log('/raw/raw_script.js was written'); 212""" 213 file_contents[os.path.normpath( 214 '/raw/components/polymer/polymer.min.js')] = """ 215""" 216 217 with fake_fs.FakeFS(file_contents): 218 project = project_module.Project( 219 [os.path.normpath('/py_vulcanize/'), 220 os.path.normpath('/tmp/'), 221 os.path.normpath('/components/'), 222 os.path.normpath('/raw/')]) 223 loader = resource_loader.ResourceLoader(project) 224 a_b_start_module = loader.LoadModule( 225 module_name='a.b.start', excluded_scripts=['\/excluded_script.js']) 226 load_sequence = project.CalcLoadSequenceForModules([a_b_start_module]) 227 228 # Check load sequence names. 229 load_sequence_names = [x.name for x in load_sequence] 230 self.assertEquals(['py_vulcanize', 231 'widget', 232 'a.b.start'], load_sequence_names) 233 234 # Check module_deps on a_b_start_module 235 def HasDependentModule(module, name): 236 return [x for x in module.dependent_modules 237 if x.name == name] 238 assert HasDependentModule(a_b_start_module, 'widget') 239 240 # Check JS generation. 241 js = generate.GenerateJS(load_sequence) 242 assert 'inline script for start.html' in js 243 assert 'inline script for widget.html' in js 244 assert '/raw/raw_script.js' in js 245 assert 'excluded_script.js' not in js 246 247 # Check HTML generation. 248 html = generate.GenerateStandaloneHTMLAsString( 249 load_sequence, title='', flattened_js_url='/blah.js') 250 assert '<dom-module id="start">' in html 251 assert 'inline script for widget.html' not in html 252 assert 'common.css' in html 253 254 def testPolymerConversion(self): 255 file_contents = {} 256 file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """ 257<!DOCTYPE html> 258<dom-module id="my-component"> 259 <template> 260 </template> 261 <script> 262 'use strict'; 263 Polymer ( { 264 is: "my-component" 265 }); 266 </script> 267</dom-module> 268""" 269 with fake_fs.FakeFS(file_contents): 270 project = project_module.Project([ 271 os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')]) 272 loader = resource_loader.ResourceLoader(project) 273 my_component = loader.LoadModule(module_name='a.b.my_component') 274 275 f = StringIO.StringIO() 276 my_component.AppendJSContentsToFile( 277 f, 278 use_include_tags_for_scripts=False, 279 dir_for_include_tag_root=None) 280 js = f.getvalue().rstrip() 281 expected_js = """ 282 'use strict'; 283 Polymer ( { 284 is: "my-component" 285 }); 286""".rstrip() 287 self.assertEquals(expected_js, js) 288 289 def testInlineStylesheetURLs(self): 290 file_contents = {} 291 file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """ 292<!DOCTYPE html> 293<style> 294.some-rule { 295 background-image: url('../something.jpg'); 296} 297</style> 298""" 299 file_contents[os.path.normpath('/tmp/a/something.jpg')] = 'jpgdata' 300 with fake_fs.FakeFS(file_contents): 301 project = project_module.Project([ 302 os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')]) 303 loader = resource_loader.ResourceLoader(project) 304 my_component = loader.LoadModule(module_name='a.b.my_component') 305 306 computed_deps = [] 307 my_component.AppendDirectlyDependentFilenamesTo(computed_deps) 308 self.assertEquals(set(computed_deps), 309 set([os.path.normpath('/tmp/a/b/my_component.html'), 310 os.path.normpath('/tmp/a/something.jpg')])) 311 312 f = StringIO.StringIO() 313 ctl = html_generation_controller.HTMLGenerationController() 314 my_component.AppendHTMLContentsToFile(f, ctl) 315 html = f.getvalue().rstrip() 316 # FIXME: This is apparently not used. 317 expected_html = """ 318.some-rule { 319 background-image: url(data:image/jpg;base64,anBnZGF0YQ==); 320} 321""".rstrip() 322