1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6'''Unit tests for grit.format.html_inline'''
7
8
9import os
10import re
11import sys
12if __name__ == '__main__':
13  sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
14
15import unittest
16
17from grit import util
18from grit.format import html_inline
19
20
21class HtmlInlineUnittest(unittest.TestCase):
22  '''Unit tests for HtmlInline.'''
23
24  def testGetResourceFilenames(self):
25    '''Tests that all included files are returned by GetResourceFilenames.'''
26
27    files = {
28      'index.html': '''
29      <!DOCTYPE HTML>
30      <html>
31        <head>
32          <link rel="stylesheet" href="test.css">
33          <link rel="stylesheet"
34              href="really-long-long-long-long-long-test.css">
35        </head>
36        <body>
37          <include src="test.html">
38          <include
39              src="really-long-long-long-long-long-test-file-omg-so-long.html">
40        </body>
41      </html>
42      ''',
43
44      'test.html': '''
45      <include src="test2.html">
46      ''',
47
48      'really-long-long-long-long-long-test-file-omg-so-long.html': '''
49      <!-- This really long named resource should be included. -->
50      ''',
51
52      'test2.html': '''
53      <!-- This second level resource should also be included. -->
54      ''',
55
56      'test.css': '''
57      .image {
58        background: url('test.png');
59      }
60      ''',
61
62      'really-long-long-long-long-long-test.css': '''
63      a:hover {
64        font-weight: bold;  /* Awesome effect is awesome! */
65      }
66      ''',
67
68      'test.png': 'PNG DATA',
69    }
70
71    source_resources = set()
72    tmp_dir = util.TempDir(files)
73    for filename in files:
74      source_resources.add(tmp_dir.GetPath(filename))
75
76    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'))
77    resources.add(tmp_dir.GetPath('index.html'))
78    self.failUnlessEqual(resources, source_resources)
79    tmp_dir.CleanUp()
80
81  def testCompressedJavaScript(self):
82    '''Tests that ".src=" doesn't treat as a tag.'''
83
84    files = {
85      'index.js': '''
86      if(i<j)a.src="hoge.png";
87      ''',
88    }
89
90    source_resources = set()
91    tmp_dir = util.TempDir(files)
92    for filename in files:
93      source_resources.add(tmp_dir.GetPath(filename))
94
95    resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'))
96    resources.add(tmp_dir.GetPath('index.js'))
97    self.failUnlessEqual(resources, source_resources)
98    tmp_dir.CleanUp()
99
100  def testInlineCSSImports(self):
101    '''Tests that @import directives in inlined CSS files are inlined too.
102    '''
103
104    files = {
105      'index.html': '''
106      <html>
107      <head>
108      <link rel="stylesheet" href="css/test.css">
109      </head>
110      </html>
111      ''',
112
113      'css/test.css': '''
114      @import url('test2.css');
115      blink {
116        display: none;
117      }
118      ''',
119
120      'css/test2.css': '''
121      .image {
122        background: url('../images/test.png');
123      }
124      '''.strip(),
125
126      'images/test.png': 'PNG DATA'
127    }
128
129    expected_inlined = '''
130      <html>
131      <head>
132      <style>
133      .image {
134        background: url('data:image/png;base64,UE5HIERBVEE=');
135      }
136      blink {
137        display: none;
138      }
139      </style>
140      </head>
141      </html>
142      '''
143
144    source_resources = set()
145    tmp_dir = util.TempDir(files)
146    for filename in files:
147      source_resources.add(tmp_dir.GetPath(util.normpath(filename)))
148
149    result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
150    resources = result.inlined_files
151    resources.add(tmp_dir.GetPath('index.html'))
152    self.failUnlessEqual(resources, source_resources)
153    self.failUnlessEqual(expected_inlined,
154                         util.FixLineEnd(result.inlined_data, '\n'))
155
156    tmp_dir.CleanUp()
157
158  def testInlineCSSLinks(self):
159    '''Tests that only CSS files referenced via relative URLs are inlined.'''
160
161    files = {
162      'index.html': '''
163      <html>
164      <head>
165      <link rel="stylesheet" href="foo.css">
166      <link rel="stylesheet" href="chrome://resources/bar.css">
167      </head>
168      </html>
169      ''',
170
171      'foo.css': '''
172      @import url(chrome://resources/blurp.css);
173      blink {
174        display: none;
175      }
176      ''',
177    }
178
179    expected_inlined = '''
180      <html>
181      <head>
182      <style>
183      @import url(chrome://resources/blurp.css);
184      blink {
185        display: none;
186      }
187      </style>
188      <link rel="stylesheet" href="chrome://resources/bar.css">
189      </head>
190      </html>
191      '''
192
193    source_resources = set()
194    tmp_dir = util.TempDir(files)
195    for filename in files:
196      source_resources.add(tmp_dir.GetPath(filename))
197
198    result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
199    resources = result.inlined_files
200    resources.add(tmp_dir.GetPath('index.html'))
201    self.failUnlessEqual(resources, source_resources)
202    self.failUnlessEqual(expected_inlined,
203                         util.FixLineEnd(result.inlined_data, '\n'))
204
205  def testFilenameVariableExpansion(self):
206    '''Tests that variables are expanded in filenames before inlining.'''
207
208    files = {
209      'index.html': '''
210      <html>
211      <head>
212      <link rel="stylesheet" href="style[WHICH].css">
213      <script src="script[WHICH].js"></script>
214      </head>
215      <include src="tmpl[WHICH].html">
216      <img src="img[WHICH].png">
217      </html>
218      ''',
219      'style1.css': '''h1 {}''',
220      'tmpl1.html': '''<h1></h1>''',
221      'script1.js': '''console.log('hello');''',
222      'img1.png': '''abc''',
223    }
224
225    expected_inlined = '''
226      <html>
227      <head>
228      <style>h1 {}</style>
229      <script>console.log('hello');</script>
230      </head>
231      <h1></h1>
232      <img src="data:image/png;base64,YWJj">
233      </html>
234      '''
235
236    source_resources = set()
237    tmp_dir = util.TempDir(files)
238    for filename in files:
239      source_resources.add(tmp_dir.GetPath(filename))
240
241    def replacer(var, repl):
242      return lambda filename: filename.replace('[%s]' % var, repl)
243
244    # Test normal inlining.
245    result = html_inline.DoInline(
246        tmp_dir.GetPath('index.html'),
247        None,
248        filename_expansion_function=replacer('WHICH', '1'))
249    resources = result.inlined_files
250    resources.add(tmp_dir.GetPath('index.html'))
251    self.failUnlessEqual(resources, source_resources)
252    self.failUnlessEqual(expected_inlined,
253                         util.FixLineEnd(result.inlined_data, '\n'))
254
255    # Test names-only inlining.
256    result = html_inline.DoInline(
257        tmp_dir.GetPath('index.html'),
258        None,
259        names_only=True,
260        filename_expansion_function=replacer('WHICH', '1'))
261    resources = result.inlined_files
262    resources.add(tmp_dir.GetPath('index.html'))
263    self.failUnlessEqual(resources, source_resources)
264
265  def testWithCloseTags(self):
266    '''Tests that close tags are removed.'''
267
268    files = {
269      'index.html': '''
270      <html>
271      <head>
272      <link rel="stylesheet" href="style1.css"></link>
273      <link rel="stylesheet" href="style2.css">
274      </link>
275      <link rel="stylesheet" href="style2.css"
276      >
277      </link>
278      <script src="script1.js"></script>
279      </head>
280      <include src="tmpl1.html"></include>
281      <include src="tmpl2.html">
282      </include>
283      <include src="tmpl2.html"
284      >
285      </include>
286      <img src="img1.png">
287      </html>
288      ''',
289      'style1.css': '''h1 {}''',
290      'style2.css': '''h2 {}''',
291      'tmpl1.html': '''<h1></h1>''',
292      'tmpl2.html': '''<h2></h2>''',
293      'script1.js': '''console.log('hello');''',
294      'img1.png': '''abc''',
295    }
296
297    expected_inlined = '''
298      <html>
299      <head>
300      <style>h1 {}</style>
301      <style>h2 {}</style>
302      <style>h2 {}</style>
303      <script>console.log('hello');</script>
304      </head>
305      <h1></h1>
306      <h2></h2>
307      <h2></h2>
308      <img src="data:image/png;base64,YWJj">
309      </html>
310      '''
311
312    source_resources = set()
313    tmp_dir = util.TempDir(files)
314    for filename in files:
315      source_resources.add(tmp_dir.GetPath(filename))
316
317    # Test normal inlining.
318    result = html_inline.DoInline(
319        tmp_dir.GetPath('index.html'),
320        None)
321    resources = result.inlined_files
322    resources.add(tmp_dir.GetPath('index.html'))
323    self.failUnlessEqual(resources, source_resources)
324    self.failUnlessEqual(expected_inlined,
325                         util.FixLineEnd(result.inlined_data, '\n'))
326
327if __name__ == '__main__':
328  unittest.main()
329