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