Commit 5d96fd38 authored by rbpotter's avatar rbpotter Committed by Commit Bot

WebUI: Update optimize_webui() to handle Polymer3

Use rollup along with existing Polymer CSS build and Uglify tools
to bundle Polymer 3 pages in optimized builds.

This will be used by the extensions Web UI in a followup CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1817293

Bug: 965770
Change-Id: Id4538b5628d1d59824300598082d89ea316eb628
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1846752
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709327}
parent 7016a4c2
......@@ -74,6 +74,11 @@ template("optimize_webui") {
invoker.insert_in_head,
]
}
if (defined(invoker.js_module_in_files)) {
inputs += [ "//chrome/browser/resources/tools/rollup_plugin.js" ]
args += [ "--js_module_in_files" ] + invoker.js_module_in_files
}
}
}
......
......@@ -55,28 +55,35 @@ _POLYMER_PATH = os.path.join(
'').replace('\\', '/')
_VULCANIZE_BASE_ARGS = [
# These files are already combined and minified.
'--exclude', 'chrome://resources/html/polymer.html',
'--exclude', 'chrome://resources/polymer/v1_0/polymer/polymer.html',
'--exclude', 'chrome://resources/polymer/v1_0/polymer/polymer-micro.html',
'--exclude', 'chrome://resources/polymer/v1_0/polymer/polymer-mini.html',
'--exclude', 'chrome://resources/polymer/v1_0/web-animations-js/' +
# These files are already combined and minified.
_BASE_EXCLUDES = [
# Common excludes for both Polymer 2 and 3.
'chrome://resources/polymer/v1_0/web-animations-js/' +
'web-animations-next-lite.min.js',
'chrome://resources/css/roboto.css',
'chrome://resources/css/text_defaults.css',
'chrome://resources/css/text_defaults_md.css',
'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html',
# Excludes applying only to Polymer 2.
'chrome://resources/html/polymer.html',
'chrome://resources/polymer/v1_0/polymer/polymer.html',
'chrome://resources/polymer/v1_0/polymer/polymer-micro.html',
'chrome://resources/polymer/v1_0/polymer/polymer-mini.html',
'chrome://resources/js/load_time_data.js',
# Excludes applying only to Polymer 3.
'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js',
'chrome://resources/js/load_time_data.m.js',
]
'--exclude', 'chrome://resources/css/roboto.css',
'--exclude', 'chrome://resources/css/text_defaults.css',
'--exclude', 'chrome://resources/css/text_defaults_md.css',
'--exclude', 'chrome://resources/js/load_time_data.js',
'--exclude', 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html',
_VULCANIZE_BASE_ARGS = [
'--inline-css',
'--inline-scripts',
'--rewrite-urls-in-templates',
'--strip-comments',
]
_URL_MAPPINGS = [
('chrome://resources/cr_components/', _CR_COMPONENTS_PATH),
('chrome://resources/cr_elements/', _CR_ELEMENTS_PATH),
......@@ -138,30 +145,98 @@ def _update_dep_file(in_folder, args, manifest):
deps_file_header = os.path.join(args.out_folder, args.html_out_files[0])
f.write(deps_file_header + ': ' + ' '.join(deps))
# Autogenerate a rollup config file so that we can import the plugin and
# pass it information about the location of the directories and files to exclude
# from the bundle.
def _generate_rollup_config(tmp_out_dir, path_to_plugin, in_path, host,
excludes):
rollup_config_file = os.path.join(tmp_out_dir, 'rollup.config.js')
excludes_string = '[\'' + '\', \''.join(excludes) + '\']'
with open(rollup_config_file, 'w') as f:
f.write('import plugin from \'%s\';\n' % path_to_plugin.replace(
'\\', '/'))
f.write('export default({\n')
f.write(' plugins: [ plugin(\'%s\', \'%s\', \'%s\', \'%s\', %s) ]\n' % (
_SRC_PATH.replace('\\', '/'),
os.path.join(_CWD, 'gen').replace('\\', '/'),
in_path.replace('\\', '/'), host, excludes_string))
f.write('});')
f.close()
return rollup_config_file;
# Create the manifest file from the sourcemap generated by rollup.
def _generate_manifest_file(
js_out_file, tmp_out_dir, in_path, manifest_out_path):
sourcemap_file = js_out_file + '.map';
with open(os.path.join(tmp_out_dir, sourcemap_file), 'r') as f:
sourcemap = json.loads(f.read())
if not 'sources' in sourcemap:
raise Exception('rollup could not construct source map')
sources = sourcemap['sources']
replaced_sources = []
for source in sources:
replaced_sources.append(
source.replace('../' + os.path.basename(in_path) + "/", ""))
manifest = { 'sources': replaced_sources };
with open(manifest_out_path, 'w') as f:
f.write(json.dumps(manifest))
f.close()
def _optimize(in_folder, args):
in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/')
out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/')
manifest_out_path = _request_list_path(out_path, args.host)
def _bundle_v3(tmp_out_dir, in_path, out_path, manifest_out_path, args,
excludes):
if not os.path.exists(tmp_out_dir):
os.makedirs(tmp_out_dir)
path_to_plugin = os.path.join(
os.path.abspath(_HERE_PATH), 'tools', 'rollup_plugin.js')
rollup_config_file = _generate_rollup_config(tmp_out_dir, path_to_plugin,
in_path, args.host, excludes)
bundled_paths = []
for index, js_module_in_file in enumerate(args.js_module_in_files):
js_out_file = args.js_out_files[index]
rollup_js_out_file = '%s.rollup.js' % js_out_file[:-3]
rollup_js_out_path = os.path.join(tmp_out_dir, rollup_js_out_file)
node.RunNode(
[node_modules.PathToRollup()] + [
'--format', 'esm',
'--input', os.path.join(in_path, js_module_in_file),
'--file', rollup_js_out_path,
'--sourcemap', '--sourcemapExcludeSources',
'--config', rollup_config_file,
'--silent',
])
# Copy the HTML file and replace the script name.
html_file = args.html_in_files[index]
html_out_file = args.html_out_files[index]
with open(os.path.join(in_path, html_file), 'r') as f:
output = f.read()
output = output.replace(js_module_in_file, js_out_file);
with open(os.path.join(out_path, html_out_file), 'w') as f:
f.write(output)
f.close()
exclude_args = []
for f in args.exclude or []:
exclude_args.append('--exclude')
exclude_args.append(f)
# Create the manifest file from the sourcemap generated by rollup.
_generate_manifest_file(rollup_js_out_file, tmp_out_dir, in_path,
manifest_out_path)
bundled_paths.append(rollup_js_out_path)
return bundled_paths
def _bundle_v2(tmp_out_dir, in_path, out_path, manifest_out_path, args,
excludes):
in_html_args = []
for f in args.html_in_files:
in_html_args.append(f)
tmp_out_dir = os.path.join(out_path, 'bundled').replace('\\', '/')
exclude_args = []
for f in excludes:
exclude_args.append('--exclude')
exclude_args.append(f);
node.RunNode(
[node_modules.PathToBundler()] +
_VULCANIZE_BASE_ARGS + _VULCANIZE_REDIRECT_ARGS + exclude_args +
[# This file is dynamically created by C++. Need to specify an exclusion
# URL for both the relative URL and chrome:// URL syntax.
'--exclude', 'strings.js',
'--exclude', 'chrome://%s/strings.js' % args.host,
[
'--manifest-out', manifest_out_path,
'--root', in_path,
'--redirect', '"chrome://%s/|%s"' % (args.host, in_path + '/'),
......@@ -170,8 +245,8 @@ def _optimize(in_folder, args):
] + in_html_args)
for index, html_file in enumerate(args.html_in_files):
with open(
os.path.join(os.path.relpath(tmp_out_dir, _CWD), html_file), 'r') as f:
with open(os.path.join(
os.path.relpath(tmp_out_dir, _CWD), html_file), 'r') as f:
output = f.read()
# Grit includes are not supported, use HTML imports instead.
......@@ -179,21 +254,21 @@ def _optimize(in_folder, args):
if args.insert_in_head:
assert '<head>' in output
# NOTE(dbeam): polymer-bundler eats <base> tags after processing. This
# undoes that by adding a <base> tag to the (post-processed) generated
# output.
# NOTE(dbeam): polymer-bundler eats <base> tags after processing.
# This undoes that by adding a <base> tag to the (post-processed)
# generated output.
output = output.replace('<head>', '<head>' + args.insert_in_head)
# Open file again with 'w' such that the previous contents are overwritten.
with open(
os.path.join(os.path.relpath(tmp_out_dir, _CWD), html_file), 'w') as f:
# Open file again with 'w' such that the previous contents are
# overwritten.
with open(os.path.join(
os.path.relpath(tmp_out_dir, _CWD), html_file), 'w') as f:
f.write(output)
f.close()
try:
crisper_html_out_paths = []
bundled_paths = []
for index, html_in_file in enumerate(args.html_in_files):
crisper_html_out_paths.append(
bundled_paths.append(
os.path.join(tmp_out_dir, args.html_out_files[index]))
js_out_file = args.js_out_files[index]
......@@ -201,29 +276,53 @@ def _optimize(in_folder, args):
node.RunNode([node_modules.PathToCrisper(),
'--source', os.path.join(tmp_out_dir, html_in_file),
'--script-in-head', 'false',
'--html', crisper_html_out_paths[index],
'--html', bundled_paths[index],
'--js', os.path.join(tmp_out_dir, js_out_file)])
return bundled_paths
def _optimize(in_folder, args):
in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/')
out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/')
manifest_out_path = _request_list_path(out_path, args.host)
tmp_out_dir = os.path.join(out_path, 'bundled').replace('\\', '/')
excludes = _BASE_EXCLUDES + [
# This file is dynamically created by C++. Need to specify an exclusion
# URL for both the relative URL and chrome:// URL syntax.
'strings.js',
'strings.m.js',
'chrome://%s/strings.js' % args.host,
'chrome://%s/strings.m.js' % args.host,
]
excludes.extend(args.exclude or [])
# Pass the JS file through Uglify and write the output to its final
try:
if args.js_module_in_files:
pcb_out_paths = [os.path.join(tmp_out_dir, f) for f in args.js_out_files]
bundled_paths = _bundle_v3(tmp_out_dir, in_path, out_path,
manifest_out_path, args, excludes)
else:
pcb_out_paths = [os.path.join(out_path, f) for f in args.html_out_files]
bundled_paths = _bundle_v2(tmp_out_dir, in_path, out_path,
manifest_out_path, args, excludes)
# Run polymer-css-build.
node.RunNode([node_modules.PathToPolymerCssBuild()] +
['--polymer-version', '2'] +
['--no-inline-includes', '-f'] +
bundled_paths + ['-o'] + pcb_out_paths)
# Pass the JS files through Uglify and write the output to its final
# destination.
for index, js_out_file in enumerate(args.js_out_files):
node.RunNode([node_modules.PathToUglify(),
os.path.join(tmp_out_dir, js_out_file),
'--comments', '"/Copyright|license|LICENSE|\<\/?if/"',
'--output', os.path.join(out_path, js_out_file)])
# Run polymer-css-build and write the output HTML files to their final
# destination.
html_out_paths = [
os.path.join(out_path, f) for f in args.html_out_files]
node.RunNode([node_modules.PathToPolymerCssBuild()] +
['--polymer-version', '2'] +
['--no-inline-includes', '-f'] +
crisper_html_out_paths + ['-o'] + html_out_paths)
finally:
shutil.rmtree(tmp_out_dir)
return manifest_out_path
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('--depfile', required=True)
......@@ -235,6 +334,7 @@ def main(argv):
parser.add_argument('--insert_in_head')
parser.add_argument('--js_out_files', nargs='*', required=True)
parser.add_argument('--out_folder', required=True)
parser.add_argument('--js_module_in_files', nargs='*')
args = parser.parse_args(argv)
# NOTE(dbeam): on Windows, GN can send dirs/like/this. When joined, you might
......
......@@ -44,11 +44,12 @@ class OptimizeWebUiTest(unittest.TestCase):
assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read()
def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file):
def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file,
js_module_in_file):
# TODO(dbeam): make it possible to _run_optimize twice? Is that useful?
assert not self._out_folder
self._out_folder = self._create_tmp_dir()
optimize_webui.main([
args = [
'--depfile', os.path.join(self._out_folder,'depfile.d'),
'--html_in_files', html_in_file,
'--html_out_files', html_out_file,
......@@ -56,7 +57,10 @@ class OptimizeWebUiTest(unittest.TestCase):
'--input', self._tmp_src_dir,
'--js_out_files', js_out_file,
'--out_folder', self._out_folder,
])
]
if (js_module_in_file):
args += [ '--js_module_in_files', js_module_in_file ];
optimize_webui.main(args)
def _write_files_to_src_dir(self):
self._write_file_to_src_dir('element.html', '<div>got here!</div>')
......@@ -69,6 +73,45 @@ class OptimizeWebUiTest(unittest.TestCase):
<link rel="import" href="element.html">
<link rel="import" href="element_in_dir/element_in_dir.html">
<script src="element.js"></script>
''')
def _write_v3_files_to_src_dir(self):
self._write_file_to_src_dir('element.js', "alert('yay');")
self._write_file_to_src_dir('element_in_dir/element_in_dir.js',
"alert('hello from element_in_dir');")
self._write_file_to_src_dir('ui.js', '''
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''')
def _write_v3_files_with_resources_to_src_dir(self):
resources_path = os.path.join(
_HERE_DIR.replace('\\', '/'), 'gen', 'ui', 'webui', 'resources', 'js',
'fake_resource.m.js')
os.makedirs(os.path.dirname(resources_path))
self._tmp_dirs.append('gen')
with open(resources_path, 'w') as tmp_file:
tmp_file.write("alert('hello from shared resource');")
self._write_file_to_src_dir('element.js', '''
import 'chrome://resources/js/action_link.js';
alert('yay');
''')
self._write_file_to_src_dir('element_in_dir/element_in_dir.js', '''
import {foo} from 'chrome://resources/js/fake_resource.m.js';
import '../strings.m.js';
alert('hello from element_in_dir');
''')
self._write_file_to_src_dir('ui.js', '''
import 'chrome://fake-host/strings.m.js';
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''')
def _check_output_html(self, out_html):
......@@ -83,27 +126,71 @@ class OptimizeWebUiTest(unittest.TestCase):
self.assertIn('yay', fast_js)
self.assertIn('hello from element_in_dir', fast_js)
def _check_output_depfile(self):
def _check_output_depfile(self, has_html):
depfile_d = self._read_out_file('depfile.d')
self.assertIn('element.html', depfile_d)
self.assertIn('element.js', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.html'),
depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
depfile_d)
if (has_html):
self.assertIn('element.html', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.html'),
depfile_d)
def testSimpleOptimize(self):
self._write_files_to_src_dir()
self._run_optimize(depfile='depfile.d',
html_in_file='ui.html',
html_out_file='fast.html',
js_out_file='fast.js')
js_out_file='fast.js',
js_module_in_file='')
fast_html = self._read_out_file('fast.html')
self._check_output_html(fast_html)
self.assertIn('<script src="fast.js"></script>', fast_html)
self._check_output_js()
self._check_output_depfile()
self._check_output_depfile(True)
def testV3SimpleOptimize(self):
self._write_v3_files_to_src_dir()
self._run_optimize(depfile='depfile.d',
html_in_file='ui.html',
html_out_file='fast.html',
js_out_file='fast.js',
js_module_in_file='ui.js')
fast_html = self._read_out_file('fast.html')
self.assertIn('<script type="module" src="fast.js"></script>', fast_html)
self.assertNotIn('<script type="module" src="ui.js"></script>', fast_html)
self._check_output_js()
self._check_output_depfile(False)
def testV3OptimizeWithResources(self):
self._write_v3_files_with_resources_to_src_dir()
self._run_optimize(depfile='depfile.d',
html_in_file='ui.html',
html_out_file='fast.html',
js_out_file='fast.js',
js_module_in_file='ui.js')
fast_html = self._read_out_file('fast.html')
self.assertIn('<script type="module" src="fast.js"></script>', fast_html)
self.assertNotIn('<script type="module" src="ui.js"></script>', fast_html)
fast_js = self._read_out_file('fast.js')
self.assertIn('yay', fast_js)
self.assertIn('hello from element_in_dir', fast_js)
self.assertIn('hello from shared resource', fast_js)
depfile_d = self._read_out_file('depfile.d')
self.assertIn('element.js', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
depfile_d)
self.assertIn(
os.path.normpath('../../../../ui/webui/resources/js/action_link.js'),
depfile_d)
self.assertIn(
os.path.normpath('../gen/ui/webui/resources/js/fake_resource.m.js'),
depfile_d)
if __name__ == '__main__':
unittest.main()
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Plugin for rollup to correctly resolve resources.
*/
const path = require('path');
const chromeResourcesUrl = 'chrome://resources/';
const polymerUrl = 'chrome://resources/polymer/v3_0/';
// TODO: Determine whether it is worth maintaining this list vs always checking
// both directories for the existence of a file.
const nonGeneratedFiles = ['cr.m.js', 'action_link.js'];
function normalizeSlashes(filepath) {
return filepath.replace(/\\/gi, '/');
}
function relativePath(from, to) {
return normalizeSlashes(path.relative(from, to));
}
function joinPaths(a, b) {
return normalizeSlashes(path.join(a, b));
}
/**
* Determines the path to |source| from the root directory based on the origin
* of the request for it. For example, if ../a/b.js is requested from
* c/d/e/f.js, the returned path will be c/d/a/b.js.
* @param {string} origin The origin of the request.
* @param {string} source The requested resource
* @return {string} Path to source from the root directory.
*/
function combinePaths(origin, source) {
const originDir = origin ? path.dirname(origin) : '';
return normalizeSlashes(path.normalize(path.join(originDir, source)));
}
export default function plugin(srcPath, genPath, rootPath, host, excludes) {
const resourcesSrcPath = joinPaths(srcPath, 'ui/webui/resources/');
const polymerSrcPath =
joinPaths(srcPath, 'third_party/polymer/v3_0/components-chromium/');
const resourcesGenPath = joinPaths(genPath, 'ui/webui/resources/');
const rootUrl = 'chrome://' + host + '/';
return {
name: 'webui-path-resolver-plugin',
resolveId(source, origin) {
// Normalize origin paths to use forward slashes.
if (origin) {
origin = normalizeSlashes(origin);
}
// Handle polymer resources
let pathFromPolymer = '';
if (source.startsWith(polymerUrl)) {
pathFromPolymer = source.slice(polymerUrl.length);
} else if (!!origin && origin.startsWith(polymerSrcPath)) {
pathFromPolymer =
combinePaths(relativePath(polymerSrcPath, origin), source);
}
if (pathFromPolymer) {
const fullPath = polymerUrl + pathFromPolymer;
if (excludes.includes(fullPath)) {
return {id: fullPath, external: true};
}
return joinPaths(polymerSrcPath, pathFromPolymer);
}
// Get path from ui/webui/resources
let pathFromResources = '';
if (source.startsWith(chromeResourcesUrl)) {
pathFromResources = source.slice(chromeResourcesUrl.length);
} else if (!!origin && origin.startsWith(resourcesSrcPath)) {
pathFromResources =
combinePaths(relativePath(resourcesSrcPath, origin), source);
} else if (!!origin && origin.startsWith(resourcesGenPath)) {
pathFromResources =
combinePaths(relativePath(resourcesGenPath, origin), source);
}
// Add prefix
if (pathFromResources) {
const fullPath = chromeResourcesUrl + pathFromResources;
if (excludes.includes(fullPath)) {
return {id: fullPath, external: true};
}
const filename = path.basename(source);
return joinPaths(
nonGeneratedFiles.includes(filename) ? resourcesSrcPath :
resourcesGenPath,
pathFromResources);
}
// Not a resources or polymer path -> should be in the root directory.
// Check if it should be excluded from the bundle.
const fullSourcePath = combinePaths(origin, source);
if (fullSourcePath.startsWith(rootPath)) {
const pathFromRoot = relativePath(rootPath, fullSourcePath);
if (excludes.includes(pathFromRoot)) {
return {id: rootUrl + pathFromRoot, external: true};
}
}
return null;
},
};
}
......@@ -30,6 +30,10 @@ def PathToPolymerCssBuild():
return _path_in_node_modules('polymer-css-build', 'bin', 'polymer-css-build')
def PathToRollup():
return _path_in_node_modules('rollup', 'dist', 'bin', 'rollup')
def PathToSvgo():
return _path_in_node_modules('svgo', 'bin', 'svgo')
......
......@@ -50,6 +50,7 @@
# encountering any dependency to that file.
import argparse
import io
import os
import re
import sys
......@@ -478,9 +479,11 @@ def main(argv):
result = _process_v3_ready(js_file, html_file)
# Reconstruct file.
with open(os.path.join(out_folder, result[1]), 'w') as f:
# Specify the newline character so that the exact same file is generated
# across platforms.
with io.open(os.path.join(out_folder, result[1]), 'w', newline='\n') as f:
for l in result[0]:
f.write(l)
f.write(unicode(l, 'utf-8'))
return
......
......@@ -23,7 +23,7 @@ class PolymerModulizerTest(unittest.TestCase):
def _read_out_file(self, file_name):
assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read()
return open(os.path.join(self._out_folder, file_name), 'rb').read()
def _run_test(self, html_type, html_file, js_file,
js_out_file, js_file_expected):
......@@ -44,7 +44,7 @@ class PolymerModulizerTest(unittest.TestCase):
actual_js = self._read_out_file(js_out_file)
expected_js = open(os.path.join(
_HERE_DIR, 'tests', js_file_expected), 'r').read()
_HERE_DIR, 'tests', js_file_expected), 'rb').read()
self.assertEquals(expected_js, actual_js)
# Test case where HTML is extracted from a Polymer2 <dom-module>.
......
......@@ -34,6 +34,7 @@
# polymer_modulizer.gni.
import argparse
import io
import os
import re
import sys
......@@ -118,9 +119,11 @@ def ProcessFile(filename, out_folder, namespace_rewrites):
out_filename = os.path.splitext(os.path.basename(filename))[0] + '.m.js'
# Reconstruct file.
with open(os.path.join(out_folder, out_filename), 'w') as f:
# Specify the newline character so that the exact same file is generated
# across platforms.
with io.open(os.path.join(out_folder, out_filename), 'w', newline='\n') as f:
for l in lines:
f.write(l)
f.write(unicode(l, 'utf-8'))
return
def main(argv):
......
......@@ -22,7 +22,7 @@ class JsModulizerTest(unittest.TestCase):
def _read_out_file(self, file_name):
assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read()
return open(os.path.join(self._out_folder, file_name), 'rb').read()
def _run_test_(self, js_file, js_file_expected, namespace_rewrites=None):
assert not self._out_folder
......@@ -40,7 +40,7 @@ class JsModulizerTest(unittest.TestCase):
js_out_file = os.path.basename(js_file).replace('.js', '.m.js')
actual_js = self._read_out_file(js_out_file)
expected_js = open(
os.path.join(_HERE_DIR, 'tests', js_file_expected), 'r').read()
os.path.join(_HERE_DIR, 'tests', js_file_expected), 'rb').read()
self.assertEquals(expected_js, actual_js)
def testSuccess_WithoutCrDefine(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment