require_relative "test_helper"

class BrowserifyTest < ActionDispatch::IntegrationTest
  def copy_example_file(filename, path = nil)
    path ||= "app/assets/javascripts"
    example_file = File.join(Rails.root, path, filename)
    new_file = File.join(Rails.root, path, filename.gsub(/\.example$/, ''))

    FileUtils.cp(example_file, new_file)
  end

  setup do
    # Reset config on each run
    Dummy::Application.config.browserify_rails.force = false

    # Reset the cache
    Rails.application.assets.cache = Sprockets::Cache::MemoryStore.new
    BrowserifyRails::BrowserifyProcessor.instance.instance_variable_set(:@dependencies, nil)
    cache_file = File.join(Rails.root, "tmp/cache/browserify-rails/browserifyinc-cache.json")
    File.delete(cache_file) if File.exist?(cache_file)

    copy_example_file "application.js.example"
    copy_example_file "foo.js.example"
    copy_example_file "nested/index.js.example"
    copy_example_file "mocha.js.coffee.example"
    copy_example_file "coffee.js.coffee.example"
    copy_example_file "node_path_based_require.js.example"
    copy_example_file "main.js.example"
    copy_example_file "require_in_a_comment.js.example"
    copy_example_file "secondary.js.example"
    copy_example_file "a_huge_library.js.example"
    copy_example_file "some_folder/answer.js.example"
    copy_example_file "browserified.js.example"
    copy_example_file "index.js.example", "node_modules/node-test-package"
    copy_example_file "simple_module.js.example"
    copy_example_file "use_import.js.example"
  end

  test "asset pipeline should serve application.js" do
    expected_output = fixture("application.out.js")

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "asset pipeline should serve foo.js" do
    expected_output = fixture("application.out.js")

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "asset pipeline should regenerate application.js when node_modules changes" do
    Dummy::Application.config.browserify_rails.evaluate_node_modules = true
    expected_output = fixture("application.out.js")

    begin
      get "/assets/application.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip

      # Ensure that Sprockets can detect the change to the file modification time
      sleep 1

      File.open(File.join(Rails.root, "node_modules/node-test-package/index.js"), "w+") do |f|
        f.puts 'module.exports = console.log("goodbye friend");'
      end

      expected_output = fixture("application.node_test_package_changed.out.js")

      get "/assets/application.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip
    ensure
      Dummy::Application.config.browserify_rails.evaluate_node_modules = false
    end
  end

  test "asset pipeline should regenerate application.js when foo.js changes" do
    expected_output = fixture("application.out.js")

    # get another js file before applciation.js to check that it appropriately clears the cached dependencies
    get "/assets/main.js"

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip

    # Ensure that Sprockets can detect the change to the file modification time
    sleep 1

    File.open(File.join(Rails.root, "app/assets/javascripts/foo.js"), "w+") do |f|
      f.puts "require('./nested');"
      f.puts "module.exports = function (n) { return n * 12 }"
    end

    expected_output = fixture("application.foo_changed.out.js")

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "asset pipeline should regenerate application.js when application.js changes" do
    expected_output = fixture("application.out.js")

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip

    # Ensure that Sprockets can detect the change to the file modification time
    sleep 1

    File.open(File.join(Rails.root, "app/assets/javascripts/application.js"), "w+") do |f|
      f.puts "var foo = require('./foo');"
      f.puts "console.log(foo(11));"
    end

    expected_output = fixture("application.changed.out.js")

    get "/assets/application.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "asset pipeline should regenerate application.js when the dependencies change and the new required file updates" do

    get "/assets/application.js"

    # Ensure that Sprockets can detect the change to the file modification time
    sleep 1

    # The dependencies of application.js change
    File.open(File.join(Rails.root, "app/assets/javascripts/application.js"), "w+") do |f|
      f.puts "var library = require('./a_huge_library.js');"
    end

    get "/assets/application.js"

    assert_response :success
    assert_match "\"THIS IS A HUGE LIBRARY\"", @response.body.strip

    # The new required js file updates
    File.open(File.join(Rails.root, "app/assets/javascripts/a_huge_library.js"), "w+") do |f|
      f.puts "module.exports = \"THIS IS A HUGE LIBRARY 2\""
    end

    get "/assets/application.js"

    assert_response :success
    assert_match "\"THIS IS A HUGE LIBRARY 2\"", @response.body.strip
  end

  test "browserifies coffee files after they have been compiled to JS" do
    expected_output = fixture("mocha.js")

    get "/assets/mocha.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "browserifies files with coffee requires" do
    get "/assets/coffee.js"

    assert_no_match /BrowserifyRails::BrowserifyError/, @response.body
  end

  test "uses NODE_PATH so files can be required non-relatively" do
    expected_output = fixture("node_path_based_require.out.js")

    get "/assets/node_path_based_require.js"

    assert_response :success

    assert_equal expected_output, @response.body.strip
    assert_equal false, @response.body.include?("Error: Cannot find module 'some_folder/answer'")
  end

  test "skips files that are already browserified" do
    get "/assets/browserified.js"

    assert_equal fixture("browserified.out.js"), @response.body.strip
  end

  test "skip files that should not be browserified" do
    get "/assets/plain.js"

    assert_equal fixture("plain.js"), @response.body.strip
  end

  test "skip files containing the word require in comments" do
    get "/assets/require_in_a_comment.js"

    assert_equal fixture("require_in_a_comment.js"), @response.body.strip
  end

  test "browserify even plain files if force == true" do
    Dummy::Application.config.browserify_rails.force = true

    get "/assets/plain.js"

    begin
      assert_equal fixture("plain.out.js"), @response.body.strip
    ensure
      Dummy::Application.config.browserify_rails.force = false
    end
  end

  test "uses config/browserify.yml to mark a module as globally available via --require" do
    expected_output = fixture("main.out.js")

    get "/assets/main.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "uses config/browserify.yml for browserification options" do
    expected_output = fixture("secondary.out.js")

    get "/assets/secondary.js"

    assert_response :success
    assert_equal expected_output, @response.body.strip
  end

  test "generates sourcemap and writes to file if --use-exorcist and set_base_path" do
    Dummy::Application.config.browserify_rails.commandline_options = "-d"
    Dummy::Application.config.browserify_rails.use_exorcist = true
    Dummy::Application.config.browserify_rails.exorcist_base_path = File.join(File.dirname(File.expand_path(__FILE__))).to_s

    begin
      expected_output = fixture("js-with-sourcemap-url.out.js")

      get "/assets/application.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip
    ensure
      Dummy::Application.config.browserify_rails.use_exorcist = false
      Dummy::Application.config.browserify_rails.commandline_options = ""
    end
  end

  test "generates sourcemap and writes to file if --use-exorcist and default_base_path" do
    Dummy::Application.config.browserify_rails.commandline_options = "-d"
    Dummy::Application.config.browserify_rails.use_exorcist = true
    Dummy::Application.config.browserify_rails.exorcist_base_path = nil

    begin
      expected_output = fixture("js-with-sourcemap-url.out.js")

      get "/assets/application.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip
    ensure
      Dummy::Application.config.browserify_rails.use_exorcist = false
      Dummy::Application.config.browserify_rails.commandline_options = ""
    end
  end

  test "throws BrowserifyError if something went wrong while executing browserify" do
    File.open(File.join(Rails.root, "app/assets/javascripts/application.js"), "w+") do |f|
      f.puts "var foo = require('./foo');"
      f.puts "var bar = require('./bar');"
    end

    get "/assets/application.js"

    assert_match /BrowserifyRails::BrowserifyError/, @response.body
  end

  test "identifies files that call import function as modules" do
    Dummy::Application.config.browserify_rails.commandline_options = "-t [ babelify --presets [ es2015 ] ]"

    begin
      expected_output = fixture("use_import.out.js")

      get "/assets/use_import.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip
    ensure
      Dummy::Application.config.browserify_rails.commandline_options = ""
    end
  end

  test "generates files under plain browserify with browserifyinc disabled" do
    Dummy::Application.config.browserify_rails.use_browserifyinc = false

    begin
      expected_output = fixture("main.out.js")

      get "/assets/main.js"

      assert_response :success
      assert_equal expected_output, @response.body.strip
    ensure
    Dummy::Application.config.browserify_rails.use_browserifyinc = true
    end
  end
end
