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