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<polymer-element name="start"> 187 <template> 188 </template> 189 <script> 190 'use strict'; 191 console.log('inline script for start.html got written'); 192 </script> 193</polymer-element> 194""" 195 file_contents[os.path.normpath('/py_vulcanize/py_vulcanize.html')] = """<!DOCTYPE html> 196""" 197 file_contents[os.path.normpath('/components/widget.html')] = """ 198<!DOCTYPE html> 199<link rel="import" href="/py_vulcanize.html"> 200<widget name="widget.html"></widget> 201<script> 202'use strict'; 203console.log('inline script for widget.html'); 204</script> 205""" 206 file_contents[os.path.normpath('/tmp/a/common.css')] = """ 207/* /tmp/a/common.css was written */ 208""" 209 file_contents[os.path.normpath('/raw/raw_script.js')] = """ 210console.log('/raw/raw_script.js was written'); 211""" 212 file_contents[os.path.normpath( 213 '/raw/components/polymer/polymer.min.js')] = """ 214""" 215 216 with fake_fs.FakeFS(file_contents): 217 project = project_module.Project( 218 [os.path.normpath('/py_vulcanize/'), 219 os.path.normpath('/tmp/'), 220 os.path.normpath('/components/'), 221 os.path.normpath('/raw/')]) 222 loader = resource_loader.ResourceLoader(project) 223 a_b_start_module = loader.LoadModule(module_name='a.b.start') 224 load_sequence = project.CalcLoadSequenceForModules([a_b_start_module]) 225 226 # Check load sequence names. 227 load_sequence_names = [x.name for x in load_sequence] 228 self.assertEquals(['py_vulcanize', 229 'widget', 230 'a.b.start'], load_sequence_names) 231 232 # Check module_deps on a_b_start_module 233 def HasDependentModule(module, name): 234 return [x for x in module.dependent_modules 235 if x.name == name] 236 assert HasDependentModule(a_b_start_module, 'widget') 237 238 # Check JS generation. 239 js = generate.GenerateJS(load_sequence) 240 assert 'inline script for start.html' in js 241 assert 'inline script for widget.html' in js 242 assert '/raw/raw_script.js' in js 243 244 # Check HTML generation. 245 html = generate.GenerateStandaloneHTMLAsString( 246 load_sequence, title='', flattened_js_url='/blah.js') 247 assert '<polymer-element name="start">' in html 248 assert 'inline script for widget.html' not in html 249 assert 'common.css' in html 250 251 def testPolymerConversion(self): 252 file_contents = {} 253 file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """ 254<!DOCTYPE html> 255<polymer-element name="my-component"> 256 <template> 257 </template> 258 <script> 259 'use strict'; 260 Polymer ( { 261 }); 262 </script> 263</polymer-element> 264""" 265 with fake_fs.FakeFS(file_contents): 266 project = project_module.Project([ 267 os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')]) 268 loader = resource_loader.ResourceLoader(project) 269 my_component = loader.LoadModule(module_name='a.b.my_component') 270 271 f = StringIO.StringIO() 272 my_component.AppendJSContentsToFile( 273 f, 274 use_include_tags_for_scripts=False, 275 dir_for_include_tag_root=None) 276 js = f.getvalue().rstrip() 277 expected_js = """ 278 'use strict'; 279 Polymer ( 'my-component', { 280 }); 281""".rstrip() 282 self.assertEquals(expected_js, js) 283 284 def testPolymerConversion2(self): 285 file_contents = {} 286 file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """ 287<!DOCTYPE html> 288<polymer-element name="my-component"> 289 <template> 290 </template> 291 <script> 292 'use strict'; 293 Polymer ( ); 294 </script> 295</polymer-element> 296""" 297 with fake_fs.FakeFS(file_contents): 298 project = project_module.Project([ 299 os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')]) 300 loader = resource_loader.ResourceLoader(project) 301 my_component = loader.LoadModule(module_name='a.b.my_component') 302 303 f = StringIO.StringIO() 304 my_component.AppendJSContentsToFile( 305 f, 306 use_include_tags_for_scripts=False, 307 dir_for_include_tag_root=None) 308 js = f.getvalue().rstrip() 309 expected_js = """ 310 'use strict'; 311 Polymer ( 'my-component'); 312""".rstrip() 313 self.assertEquals(expected_js, js) 314 315 def testInlineStylesheetURLs(self): 316 file_contents = {} 317 file_contents[os.path.normpath('/tmp/a/b/my_component.html')] = """ 318<!DOCTYPE html> 319<style> 320.some-rule { 321 background-image: url('../something.jpg'); 322} 323</style> 324""" 325 file_contents[os.path.normpath('/tmp/a/something.jpg')] = 'jpgdata' 326 with fake_fs.FakeFS(file_contents): 327 project = project_module.Project([ 328 os.path.normpath('/py_vulcanize/'), os.path.normpath('/tmp/')]) 329 loader = resource_loader.ResourceLoader(project) 330 my_component = loader.LoadModule(module_name='a.b.my_component') 331 332 computed_deps = [] 333 my_component.AppendDirectlyDependentFilenamesTo(computed_deps) 334 self.assertEquals(set(computed_deps), 335 set([os.path.normpath('/tmp/a/b/my_component.html'), 336 os.path.normpath('/tmp/a/something.jpg')])) 337 338 f = StringIO.StringIO() 339 ctl = html_generation_controller.HTMLGenerationController() 340 my_component.AppendHTMLContentsToFile(f, ctl) 341 html = f.getvalue().rstrip() 342 # FIXME: This is apparently not used. 343 expected_html = """ 344.some-rule { 345 background-image: url(data:image/jpg;base64,anBnZGF0YQ==); 346} 347""".rstrip() 348