# frozen_string_literal: true
require "rails_helper"

RSpec.describe ActiveAdmin::Views::AttributesTable do
  describe "creating with the dsl" do
    let(:helpers) { mock_action_view }

    let(:post) do
      post = Post.new title: "Hello World", body: nil
      allow(post).to receive(:id) { 1 }
      allow(post).to receive(:new_record?) { false }
      post
    end

    let(:assigns) { { post: post } }

    # Loop through a few different ways to make the same table
    # and ensure that they produce the same results
    {
      "when attributes are passed in to the builder methods" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post, :id, :title, :body
        end
      },
      "when attributes are built using the block" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post do
            rows :id, :title, :body
          end
        end
      },
      "when each attribute is passed in by itself" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post do
            row :id
            row :title
            row :body
          end
        end
      },
      "when you create each row with a custom block" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post do
            row("Id") { post.id }
            row("Title") { post.title }
            row("Body") { post.body }
          end
        end
      },
      "when you create each row with a string and symbol" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post do
            row "Id", :id
            row "Title", :title
            row "Body", :body
          end
        end
      },
      "when you create each row with a custom block that returns nil" => proc {
        render_arbre_component(assigns) do
          attributes_table_for post do
            row("Id") { text_node post.id; nil }
            row("Title") { text_node post.title; nil }
            row("Body") { text_node post.body; nil }
          end
        end
      },
    }.each do |context_title, table_declaration|
      context context_title do
        let(:table) { instance_eval(&table_declaration) }

        it "should render a div wrapper with the class '.attributes-table'" do
          expect(table.tag_name).to eq "div"
          expect(table.attr(:class)).to include("attributes-table")
        end

        it "should add id and type class" do
          expect(table.class_names).to include("post")
          expect(table.id).to eq "attributes_table_post_1"
        end

        it "should render 3 rows" do
          expect(table.find_by_tag("tr").size).to eq 3
        end

        describe "rendering the rows" do
          [
            ["Id", "1"],
            ["Title", "Hello World"],
            ["Body", "<span class=\"attributes-table-empty-value\">Empty</span>"]
          ].each_with_index do |(title, content), i|
            describe "for #{title}" do
              let(:current_row) { table.find_by_tag("tr")[i] }

              it "should have the title '#{title}'" do
                expect(current_row.find_by_tag("th").first.content).to eq title
              end

              it "should have the content '#{content}'" do
                expect(current_row.find_by_tag("td").first.content.chomp.strip).to eq content
              end
            end
          end
        end # describe rendering rows
      end
    end # describe dsl styles

    it "should add a data attribute for each row based on the column name" do
      table = render_arbre_component(assigns) do
        attributes_table_for(post) do
          row :title
          row :created_at
        end
      end

      expect(table.find_by_tag("tr").first.to_s.split("\n").first.lstrip).
        to eq '<tr data-row="title">'

      expect(table.find_by_tag("tr").last.to_s.split("\n").first.lstrip).
        to eq '<tr data-row="created_at">'
    end

    it "should allow html options for the row itself" do
      table = render_arbre_component(assigns) do
        attributes_table_for(post) do
          row("Wee", class: "custom_row", style: "custom_style") {}
        end
      end
      expect(table.find_by_tag("tr").first.to_s.split("\n").first.lstrip).
        to eq '<tr class="custom_row" style="custom_style" data-row="wee">'
    end

    it "should allow html content inside the attributes table" do
      table = render_arbre_component(assigns) do
        attributes_table_for(post) do
          row("ID") { span(post.id, class: "id") }
        end
      end
      expect(table.find_by_tag("td").first.content.chomp.strip).to eq "<span class=\"id\">1</span>"
    end

    context "an attribute ending in _id" do
      before do
        post.foo_id = 23
        post.author = User.new username: "john_doe", first_name: "John", last_name: "Doe"
      end
      it "should call the association if one exists" do
        table = render_arbre_component assigns do
          attributes_table_for post, :author
        end
        expect(table.find_by_tag("th").first.content).to eq "Author"
        expect(table.find_by_tag("td").first.content).to eq "John Doe"
      end

      it "should not attempt to call a nonexistent association" do
        table = render_arbre_component assigns do
          attributes_table_for post, :foo_id
        end
        expect(table.find_by_tag("th").first.content).to eq "Foo"
        expect(table.find_by_tag("td").first.content).to eq "23"
      end
    end

    context "with a collection" do
      let(:posts) do
        [Post.new(title: "Hello World", id: 1), Post.new(title: "Multi Column", id: 2)].each_with_index do |post, index|
          allow(post).to receive(:id).and_return(index + 1)
          allow(post).to receive(:new_record?).and_return(false)
        end
      end

      let(:assigns) { { posts: posts } }

      let(:table) do
        render_arbre_component(assigns) do
          attributes_table_for posts, :id, :title
        end
      end

      it "does not set id on the table" do
        expect(table.attr(:id)).to eq nil
      end

      context "colgroup" do
        let(:cols) { table.find_by_tag "col" }

        it "contains a col for each record (plus headers)" do
          expect(cols.size).to eq(2 + 1)
        end

        it "assigns an id to each col" do
          cols[1..-1].each_with_index do |col, index|
            expect(col.id).to eq "attributes_table_post_#{index + 1}"
          end
        end
      end

      context "when rendering the rows" do
        it "should contain 3 columns" do
          expect(table.find_by_tag("tr").first.children.size).to eq 3
        end

        [
          ["Id", "1", "2"],
          ["Title", "Hello World", "Multi Column"],
        ].each_with_index do |set, i|
          describe "for #{set[0]}" do
            let(:title) { set[0] }
            let(:current_row) { table.find_by_tag("tr")[i] }

            it "should have the title '#{set[0]}'" do
              expect(current_row.find_by_tag("th").first.content).to eq title
            end

            context "with defined attribute name translation" do
              it "should have the translated attribute name in the title" do
                with_translation %i[activerecord attributes post title], "Translated Title" do
                  with_translation %i[activerecord attributes post id], "Translated Id" do
                    expect(current_row.find_by_tag("th").first.content).to eq "Translated #{title}"
                  end
                end
              end
            end

            set[1..-1].each_with_index do |content, index|
              it "column #{index} should have the content '#{content}'" do
                expect(current_row.find_by_tag("td")[index].content.chomp.strip).to eq content
              end
            end
          end
        end
      end # describe rendering rows
    end # with a collection

    context "when using a single Hash" do
      let(:table) do
        render_arbre_component nil, helpers do
          attributes_table_for foo: 1, bar: 2 do
            row :foo
            row :bar
          end
        end
      end
      it "should render" do
        expect(table.find_by_tag("th")[0].content).to eq "Foo"
        expect(table.find_by_tag("th")[1].content).to eq "Bar"
        expect(table.find_by_tag("td")[0].content).to eq "1"
        expect(table.find_by_tag("td")[1].content).to eq "2"
      end
    end

    context "when using an Array of Hashes" do
      let(:table) do
        render_arbre_component nil, helpers do
          attributes_table_for [{ foo: 1 }, { foo: 2 }] do
            row :foo
          end
        end
      end
      it "should render" do
        expect(table.find_by_tag("th")[0].content).to eq "Foo"
        expect(table.find_by_tag("td")[0].content).to eq "1"
        expect(table.find_by_tag("td")[1].content).to eq "2"
      end
    end

    context "when using custom string labels with acronyms" do
      let(:table) do
        render_arbre_component(assigns) do
          attributes_table_for post do
            row("Registration ID") { post.id }
            row("LMnO") { post.title }
          end
        end
      end

      it "should preserve acronym capitalization in custom string labels" do
        rows = table.find_by_tag("tr")

        expect(rows[0].find_by_tag("th").first.content).to eq "Registration ID"
        expect(rows[1].find_by_tag("th").first.content).to eq "LMnO"

        expect(rows[0].find_by_tag("th").first.content).not_to eq "Registration Id"
        expect(rows[1].find_by_tag("th").first.content).not_to eq "L Mn O"
      end
    end
  end
end
