require "spec_helper"

describe CssCleaner do
  include CssCleaner

  describe ".clean_css_code" do
    context "when cleaning Skin CSS" do
      context "with custom property declaration" do
        it "allows custom property name with lowercase letters" do
          skin = build(:skin, css: ":root { --white: #fff; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  --white: #fff;\n}\n\n")
        end

        it "allows custom property name with numbers" do
          skin = build(:skin, css: ":root { --100: 100%; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  --100: 100%;\n}\n\n")
        end

        it "allows custom property name with dashes" do
          skin = build(:skin, css: ":root { ---: transparent; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  ---: transparent;\n}\n\n")
        end

        it "allows custom property name with underscores" do
          skin = build(:skin, css: ":root { --__: rgba(255, 255, 255, 0); }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  --__: rgba(255, 255, 255, 0);\n}\n\n")
        end

        # https://github.com/premailer/css_parser/blob/2ef7dcaaf9ceaba6652d67a875e4df5e76d8950f/lib/css_parser/rule_set.rb#L228-L232
        it "downcases uppercase letters in custom property name" do
          skin = build(:skin, css: ":root { --RAINBOW: #fff; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  --rainbow: #fff;\n}\n\n")
        end

        %w[url URL].each do |function_name|
          it "strips custom property and returns error when value uses #{function_name}() function" do
            skin = build(:skin, css: ":root { --art: #{function_name}(\"https://example.com/img.png\"); }")
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("--art in :root cannot have the value #{function_name}(\"https://example.com/img.png\"), sorry!")
          end
        end

        it "strips custom property and returns error when value uses double quotation marks" do
          skin = build(:skin, css: ":root { --serif: \"Times New Roman\" };")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("--serif in :root cannot have the value \"Times New Roman\", sorry!")
        end

        it "strips custom property and returns error when value uses single quotation marks" do
          skin = build(:skin, css: ":root { --sans-serif: 'Lucida Sans' };")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("--sans-serif in :root cannot have the value 'Lucida Sans', sorry!")
        end

        it "allows shorthand-style values" do
          skin = build(:skin, css: ":root { --heading: small-caps 1.125rem Georgia, Times New Roman, serif; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq(":root {\n  --heading: small-caps 1.125rem Georgia, Times New Roman, serif;\n}\n\n")
        end

        it "allows var() function as value" do
          skin = build(:skin, css: "#header { --nav_color: var(--brand-color); }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq("#header {\n  --nav_color: var(--brand-color);\n}\n\n")
        end

        it "strips custom property and returns error when shorthand-style value includes url() function" do
          skin = build(:skin, css: ":root { --background: #900 url(\"https://example.com/img.png\"); }")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("--background in :root cannot have the value #900 url(\"https://example.com/img.png\"), sorry!")
        end

        it "strips custom property and returns error when shorthand-style value includes quotation marks" do
          skin = build(:skin, css: ":root { --heading: small-caps 1.125rem Georgia, \"Times New Roman\", serif; }")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("--heading in :root cannot have the value small-caps 1.125rem Georgia, \"Times New Roman\", serif, sorry!")
        end

        it "strips custom property with disallowed characters in name and returns error" do
          skin = build(:skin, css: "#footer, #header { --#hash: absolute; }")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("The --#hash custom property in #footer, #header has an invalid name. Names can contain lowercase letters (a-z) in the English alphabet, numerals zero to nine (0-9), dashes (-), and underscores (_).")
        end

        it "strips invalid property and returns error when property contains text resembling custom property name" do
          skin = build(:skin, css: ":root { color--heading: absolute; }")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("We don't currently allow the CSS property color--heading -- please notify Support if you think this is an error.")
        end

        # Using a property from SUPPORTED_CSS_SHORTHAND_PROPERTIES allows anything, e.g., font-salmon, awkwardpause, background_witches
        it "allows invalid variation of shorthand property when property contains text resembling custom property name" do
          skin = build(:skin, css: ":root { background--heading: absolute; }")
          expect(skin.save).to be_truthy
          expect(skin.css).to eq(":root {\n  background--heading: absolute;\n}\n\n")
        end
      end

      context "with var() function" do
        it "allows simple var() function" do
          skin = build(:skin, css: "div { color: var(--black) }")
          expect(skin.save).to be_truthy
          expect(skin.css).to eq("div {\n  color: var(--black);\n}\n\n")
        end

        it "downcases var() function" do
          skin = build(:skin, css: "div { color: VAR(--PURPLE); display: var(--RANDOMThing); height: var(--SHORT); }")
          expect(skin.save).to be_truthy
          expect(skin.css).to eq("div {\n  color: var(--purple);\n  display: var(--randomthing);\n  height: var(--short);\n}\n\n")
        end

        %w[var VAR].each do |function_name|
          it "strips #{function_name}() function from content property and returns error" do
            skin = build(:skin, css: "p:before { content: #{function_name}(--text) }")
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("content in p:before cannot have the value #{function_name}(--text), sorry!")
          end

          it "strips #{function_name}() function from font-family property and returns error" do
            skin = build(:skin, css: ".heading { font-family: #{function_name}(--serif) }")
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("font-family in .heading cannot have the value #{function_name}(--serif), sorry!")
          end

          it "strips #{function_name}() function with fallbacks and returns error" do
            skin = build(:skin, css: "p { color: #{function_name}(--blue, #fff) }")
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("color in p cannot have the value #{function_name}(--blue, #fff), sorry!")
          end

          it "strips #{function_name}() function with unclosed parentheses and returns error" do
            skin = build(:skin, css: "p { color: #{function_name}(--blue }")
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("There don't seem to be any rules for p.")
          end
        end

        context "when used in shorthand declaration" do
          it "allows simple var() function" do
            skin = build(:skin, css: "div { font: var(--black) }")
            expect(skin.save).to be_truthy
            expect(skin.css).to eq("div {\n  font: var(--black);\n}\n\n")
          end

          it "allows multiple simple var() functions" do
            skin = build(:skin, css: "blockquote { border: var(--border-width) var(--Border-Style) var(--color) }")
            expect(skin.save).to be_truthy
            expect(skin.css).to eq("blockquote {\n  border: var(--border-width) var(--border-style) var(--color);\n}\n\n")
          end

          it "downcases var() function" do
            skin = build(:skin, css: "div { border: var(--THICK) solid var(--BRIGHTblue); margin: 0 VAR(--wide) }")
            expect(skin.save).to be_truthy
            expect(skin.css).to eq("div {\n  border: var(--thick) solid var(--brightblue);\n  margin: 0 var(--wide);\n}\n\n")
          end
        end
      end

      context "with box-shadow property" do
        it "allows single value" do
          skin = build(:skin, css: "div { box-shadow:inset 1px 1px 2px #000; }")
          expect(skin.save).to be_truthy
          expect(skin.css).to eq("div {\n  box-shadow: inset 1px 1px 2px #000;\n}\n\n")
        end

        it "allows multiple values" do
          skin = build(:skin, css: "div { box-shadow: 3px 3px rgba(0, 0, 0, 0.5) inset, -1em 0 0.4em olive }")
          expect(skin.save).to be_truthy
          expect(skin.css).to eq("div {\n  box-shadow: 3px 3px rgba(0, 0, 0, 0.5) inset, -1em 0 0.4em olive;\n}\n\n")
        end
      end

      it "allows !important keyword" do
        skin = build(:skin, css: "div { color: #ddd !important; }")
        expect(skin.save).to be_truthy
        expect(skin.css).to eq("div {\n  color: #ddd !important;\n}\n\n")
      end
    end

    context "when cleaning WorkSkin CSS" do
      it "strips valid custom properties and returns error" do
        skin = build(:work_skin, css: "#workskin { --background: #fff; }")
        expect(skin.save).to be_falsey
        expect(skin.css).to eq("")
        expect(skin.errors[:base]).to include("Custom properties are not allowed in work skins.")
      end

      context "with var() function as value" do
        {
          "strips var() function and returns error" => "p { color: var(--puce) ; }",
          "strips var() function with uppercase letters in variable and returns error" => "span { border-color:var(--SOMEcolor) }",
          "strips var() function in shorthand value and returns error" => "#id { border: var(--border-width) var(--Border-Style) #000; }",
          "strips VAR() function and returns error" => ".class { width: VAR(--narrow) }",
          "strips VAR() function in shorthand value and returns error" => "p:not(.class) { border: VAR(--border-width) VAR(--Border-Style) #000; }"
        }.each_pair do |description, css|
          it description do
            skin = build(:work_skin, css: css)
            expect(skin.save).to be_falsey
            expect(skin.css).to eq("")
            expect(skin.errors[:base]).to include("The var() function is not allowed in work skins.")
          end
        end
      end

      context "with position property" do
        it "strips value fixed" do
          skin = build(:work_skin, css: "div { position: fixed; }")
          expect(skin.save).to be_falsey
          expect(skin.css).to eq("")
          expect(skin.errors[:base]).to include("The position property in div cannot have the value fixed in work skins, sorry!")
        end

        it "allows other values" do
          skin = build(:work_skin, css: "div { position: absolute; }")
          expect(skin.save).to be_truthy
          expect(skin.reload.css).to eq("#workskin div {\n  position: absolute;\n}\n\n")
        end
      end

      it "prefixes selectors with #workskin" do
        skin = create(:work_skin, css: "p { color: red; }")
        expect(skin.reload.css).to include("#workskin p")
      end
    end
  end
end
