Home | History | Annotate | Download | only in py_vulcanize
      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 
      5 import os
      6 import unittest
      7 import StringIO
      8 
      9 from py_vulcanize import fake_fs
     10 from py_vulcanize import generate
     11 from py_vulcanize import html_generation_controller
     12 from py_vulcanize import html_module
     13 from py_vulcanize import parse_html_deps
     14 from py_vulcanize import project as project_module
     15 from py_vulcanize import resource
     16 from py_vulcanize import resource_loader as resource_loader
     17 
     18 
     19 class 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 
     37 class 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 
     72 class 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 
    177 class 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';
    204 console.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')] = """
    211 console.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