• 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
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