chromium/ui/webui/resources/tools/bundle_js_test.py

#!/usr/bin/env python3
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import json
import bundle_js
import os
import shutil
import tempfile
import unittest

_CWD = os.getcwd()
_HERE_DIR = os.path.dirname(__file__)


class BundleJsTest(unittest.TestCase):

  def setUp(self):
    self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR)
    self.maxDiff = None

  def tearDown(self):
    shutil.rmtree(self._out_folder)

  def _read_file(self, file):
    with open(file, 'r') as f:
      return f.read()

  def _read_out_file(self, file_name):
    assert self._out_folder
    return self._read_file(os.path.join(self._out_folder, file_name))

  def _check_dep_file(self, paths_from_test_dir, depfile_content):
    for path in paths_from_test_dir:
      abs_path = os.path.join(_HERE_DIR, 'tests', 'bundle_js', path)
      rel_path = os.path.relpath(abs_path, _CWD)
      self.assertIn(os.path.normpath(rel_path), depfile_content)

  def _check_bundle_output(self, expected_bundle_name, actual_bundle_name):
    expected_js = self._read_file(
        os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'expected',
                     expected_bundle_name))
    actual_js = self._read_file(
        os.path.join(self._out_folder, actual_bundle_name))
    self.assertMultiLineEqual(expected_js, actual_js)

  def _check_dep_file(self, paths_from_test_dir, depfile_content):
    for path in paths_from_test_dir:
      abs_path = os.path.join(_HERE_DIR, 'tests', 'bundle_js', path)
      rel_path = os.path.relpath(abs_path, _CWD)
      self.assertIn(os.path.normpath(rel_path), depfile_content)

  def _get_external_paths_args(self):
    resources_path = os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'resources')
    custom_dir_foo = os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'external',
                                  'foo')
    custom_dir_baz = os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'external',
                                  'baz')
    custom_dir_bar = os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'external',
                                  'bar')
    return [
        '--external_paths',
        '//resources|%s' % resources_path,
        'chrome://resources|%s' % resources_path,
        'chrome-untrusted://resources|%s' % resources_path,
        # Test case where an exact URL is redirected, not just a prefix.
        'some-fake-scheme://foo/baz.js|%s' %
        os.path.abspath(os.path.join(custom_dir_baz, 'baz.js')),
        'some-fake-scheme://foo|%s' % os.path.abspath(custom_dir_foo),
        'some-fake-scheme://bar|%s' % os.path.abspath(custom_dir_bar),
    ]

  def _run_bundle(self, custom_args):
    input_path = os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'src')

    args = [
        '--depfile',
        os.path.join(self._out_folder, 'depfile.d'),
        '--target_name',
        'dummy_target_name',
        '--input',
        input_path,
        '--out_folder',
        self._out_folder,
    ] + self._get_external_paths_args() + custom_args
    bundle_js.main(args)

  def testSimpleBundle(self):
    args = [
        '--host',
        'fake-host',
        '--js_module_in_files',
        'foo_ui.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('foo_ui.rollup.js', 'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(['src/foo.js', 'src/subdir/baz.js'], depfile_d)

  def testBundleWithResources(self):
    resources_path = './tests/bundle_js/resources'
    args = [
        '--host',
        'fake-host',
        '--js_module_in_files',
        'foo_ui.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('foo_ui.rollup.js', 'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(
        ['resources/foo_resource.js', 'resources/bar_resource.js'], depfile_d)

  def testMultiBundleBundle(self):
    args = [
        '--host',
        'fake-host',
        '--js_module_in_files',
        'foo_ui.js',
        'bar_ui.js',
        '--out-manifest',
        os.path.join(self._out_folder, 'out_manifest.json'),
    ]
    self._run_bundle(args)

    # Check that the shared element is in the shared bundle and the non-shared
    # elements are in the individual bundles.
    self._check_bundle_output('foo_ui_multi_bundle.rollup.js',
                              'foo_ui.rollup.js')
    self._check_bundle_output('bar_ui.rollup.js', 'bar_ui.rollup.js')
    self._check_bundle_output('shared.rollup.js', 'shared.rollup.js')

    # All 3 JS files should be in the depfile.
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(['src/foo.js', 'src/bar.js', 'src/subdir/baz.js'],
                         depfile_d)

    manifest = json.loads(self._read_out_file('out_manifest.json'))
    self.assertEqual(3, len(manifest['files']))
    self.assertTrue('foo_ui.rollup.js' in manifest['files'])
    self.assertTrue('bar_ui.rollup.js' in manifest['files'])
    self.assertTrue('shared.rollup.js' in manifest['files'])

    self.assertEqual(
        os.path.normpath(os.path.relpath(self._out_folder, _CWD)),
        os.path.relpath(manifest['base_dir'], _CWD))

  def testSimpleBundleExcludes(self):
    args = [
        '--host',
        'chrome-extension://myextensionid/',
        '--js_module_in_files',
        'foo_ui.js',
        '--exclude',
        'foo.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('foo_ui_excludes.rollup.js', 'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(['src/subdir/baz.js'], depfile_d)
    self.assertNotIn('src/foo.js', depfile_d)

  # Tests that bundling resources for an untrusted UI can successfully exclude
  # resources imported from both chrome-untrusted://resources and scheme
  # relative paths.
  def testSimpleBundleExcludesResources(self):
    args = [
        '--host',
        'chrome-untrusted://fake-host',
        '--js_module_in_files',
        'foo_ui.js',
        '--exclude',
        '//resources/bar_resource.js',
        'chrome-untrusted://resources/foo_untrusted.js',
    ]
    self._run_bundle(args)

    output_js = self._read_out_file('foo_ui.rollup.js')
    self._check_bundle_output('foo_ui_excludes_resources.rollup.js',
                              'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self.assertNotIn('resources/foo_untrusted.js', depfile_d)
    self.assertNotIn('resources/bar_resource.js', depfile_d)

  # Tests that bundling resources for an untrusted UI successfully bundles
  # resources from both chrome-untrusted://resources and //resources.
  def testSimpleBundleUntrustedResources(self):
    args = [
        '--host',
        'chrome-untrusted://fake-host',
        '--js_module_in_files',
        'foo_ui.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('foo_ui.rollup.js', 'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(
        ['resources/foo_untrusted.js', 'resources/bar_resource.js'], depfile_d)

  def testBundleWithCustomLayeredPaths(self):
    args = [
        '--host',
        'fake-host',
        '--js_module_in_files',
        'foo_ui.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('foo_ui.rollup.js', 'foo_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(
        ['src/foo.js', 'external/foo/foo.js', 'external/bar/bar.js'], depfile_d)

  def testBundleWithBundleSubpath(self):
    args = [
        '--host',
        'fake-host',
        '--js_module_in_files',
        'subdir/baz_ui.js',
    ]
    self._run_bundle(args)

    self._check_bundle_output('baz_ui.rollup.js', 'subdir/baz_ui.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(['src/foo.js', 'src/subdir/baz.js'], depfile_d)

  def testSimpleBundleWithCustomConfig(self):
    args = [
        '--host', 'fake-host', '--js_module_in_files', 'bar_wrapper.js',
        '--rollup_config',
        os.path.join(_HERE_DIR, 'tests', 'bundle_js', 'dummy.config.mjs')
    ]
    self._run_bundle(args)

    self._check_bundle_output('bar_wrapper.rollup.js', 'bar_wrapper.rollup.js')
    depfile_d = self._read_out_file('depfile.d')
    self._check_dep_file(['src/bar.js'], depfile_d)


if __name__ == '__main__':
  unittest.main()