require 'spec_helper'

module Synvert::Core::NodeQuery
  RSpec.describe Parser do
    let(:parser) { described_class.new }

    def assert_parser(source)
      expect(parser.parse(source).to_s).to eq source
    end

    it 'parses one selector' do
      source = '.send[message=:create]'
      assert_parser(source)
    end

    it 'parses two selectors' do
      source = '.class[name=Synvert] .def[name="foobar"]'
      assert_parser(source)
    end

    it 'parses three selectors' do
      source = '.class[name=Synvert] .def[name="foobar"] .send[message=create]'
      assert_parser(source)
    end

    it 'parses child selector' do
      source = '.class[name=Synvert] > .def[name="foobar"]'
      assert_parser(source)
    end

    it 'parses :first-child' do
      source = '.class .def:first-child'
      assert_parser(source)
    end

    it 'parses :last-child' do
      source = '.class .def:last-child'
      assert_parser(source)
    end

    it 'parses :nth-child(n)' do
      source = '.class .def:nth-child(2)'
      assert_parser(source)
    end

    it 'parses :nth-last-child(n)' do
      source = '.class .def:nth-last-child(2)'
      assert_parser(source)
    end

    it 'parses :has selector' do
      source = '.class:has(> .def)'
      assert_parser(source)
    end

    it 'parses :not_has selector' do
      source = '.class:not_has(> .def)'
      assert_parser(source)
    end

    it 'parses multiple attributes' do
      source = '.send[receiver=nil][message=:create]'
      assert_parser(source)
    end

    it 'parses nested attributes' do
      source = '.send[receiver.message=:create]'
      assert_parser(source)
    end

    it 'parses selector value' do
      source = '.send[receiver=.send[message=:create]]'
      assert_parser(source)
    end

    it 'parses not equal operator' do
      source = '.send[receiver=.send[message!=:create]]'
      assert_parser(source)
    end

    it 'parses greater than operator' do
      source = '.send[receiver=.send[arguments.size>1]]'
      assert_parser(source)
    end

    it 'parses greater than or equal operator' do
      source = '.send[receiver=.send[arguments.size>=1]]'
      assert_parser(source)
    end

    it 'parses less than operator' do
      source = '.send[receiver=.send[arguments.size<1]]'
      assert_parser(source)
    end

    it 'parses less than or equal operator' do
      source = '.send[receiver=.send[arguments.size<=1]]'
      assert_parser(source)
    end

    it 'parses in operator' do
      source = '.def[name in (foo, bar)]'
      assert_parser(source)
    end

    it 'parses not_in operator' do
      source = '.def[name not in (foo, bar)]'
      assert_parser(source)
    end

    it 'parses includes operator' do
      source = '.def[arguments includes &block]'
      assert_parser(source)
    end

    it 'parses empty string' do
      source = '.send[arguments.first=""]'
      assert_parser(source)
    end

    describe '#query_nodes' do
      let(:node) {
        parse(<<~EOS)
          class Synvert
            def foo
              FactoryBot.create(:user, name: 'foo')
            end

            def bar
              FactoryBot.create(:user, name: 'bar')
            end

            def foobar(a, b)
              { a: a, b: b }
              foo.merge(bar)
              arr[index]
            end
          end
        EOS
      }

      it 'matches class node' do
        expression = parser.parse('.class[name=Synvert]')
        expect(expression.query_nodes(node)).to eq [node]
      end

      it 'matches def node' do
        expression = parser.parse('.def')
        expect(expression.query_nodes(node)).to eq node.body
      end

      it 'matches first def node' do
        expression = parser.parse('.def:first-child')
        expect(expression.query_nodes(node)).to eq [node.body.first]
      end

      it 'matches nested first node' do
        expression = parser.parse('.def[arguments.size=0] .send:first-child')
        expect(expression.query_nodes(node)).to eq [node.body.first.body.first, node.body.second.body.first]
      end

      it 'matches last def node' do
        expression = parser.parse('.def:last-child')
        expect(expression.query_nodes(node)).to eq [node.body.last]
      end

      it 'matches nth-child node' do
        expression = parser.parse('.def:nth-child(2)')
        expect(expression.query_nodes(node)).to eq [node.body.second]
      end

      it 'matches nth-last-child node' do
        expression = parser.parse('.def:nth-last-child(2)')
        expect(expression.query_nodes(node)).to eq [node.body[-2]]
      end

      it 'matches not equal' do
        expression = parser.parse('.def[name!=foobar]')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
      end

      it 'matches in' do
        expression = parser.parse('.def[name IN (foo, bar)]')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
      end

      it 'matches not in' do
        expression = parser.parse('.def[name NOT IN (foo, bar)]')
        expect(expression.query_nodes(node)).to eq [node.body.last]
      end

      it 'matches includes' do
        expression = parser.parse('.def[arguments INCLUDES a]')
        expect(expression.query_nodes(node)).to eq [node.body.last]
      end

      it 'matches equal array' do
        expression = parser.parse('.def[arguments=(a, b)]')
        expect(expression.query_nodes(node)).to eq [node.body.last]

        expression = parser.parse('.def[arguments=(b, a)]')
        expect(expression.query_nodes(node)).to eq []
      end

      it 'matches not equal array' do
        expression = parser.parse('.def[arguments!=(a, b)]')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]

        expression = parser.parse('.def[arguments!=(b, a)]')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second, node.body.last]
      end

      it 'matches descendant node' do
        expression = parser.parse('.class .send[message=:create]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
      end

      it 'matches three level descendant node' do
        expression = parser.parse('.class .def .send[message=:create]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
      end

      it 'matches child node' do
        expression = parser.parse('.def > .send[message=:create]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
      end

      it 'matches next sibling node' do
        expression = parser.parse('.def[name=foo] + .def[name=bar]')
        expect(expression.query_nodes(node)).to eq [node.body.second]
      end

      it 'matches sebsequent sibling node' do
        expression = parser.parse('.def[name=foo] ~ .def[name=foobar]')
        expect(expression.query_nodes(node)).to eq [node.body.last]
      end

      it 'matches has selector' do
        expression = parser.parse('.def:has(> .send[receiver=FactoryBot])')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
      end

      it 'matches not_has selector' do
        expression = parser.parse('.def:not_has(> .send[receiver=FactoryBot])')
        expect(expression.query_nodes(node)).to eq [node.body.last]
      end

      it 'matches arguments.size' do
        expression = parser.parse('.send[arguments.size=2]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
        expression = parser.parse('.send[arguments.size>2]')
        expect(expression.query_nodes(node)).to eq []
        expression = parser.parse('.send[arguments.size>=2]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
      end

      it 'matches arguments' do
        expression = parser.parse('.send[arguments=[size=2][first=.sym][last=.hash]]')
        expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
      end

      it 'matches regexp value' do
        expression = parser.parse('.def[name=~/foo/]')
        expect(expression.query_nodes(node)).to eq [node.body.first, node.body.last]
        expression = parser.parse('.def[name!~/bar/]')
        expect(expression.query_nodes(node)).to eq [node.body.first]
      end

      it 'matches attribute value' do
        expression = parser.parse('.pair[key={{value}}]')
        expect(expression.query_nodes(node)).to eq node.body.last.body.first.children
      end

      it 'matches identifier' do
        expression = parser.parse('.send[receiver=foo][message=merge]')
        expect(expression.query_nodes(node)).to eq [node.body.last.body.second]
      end

      it 'matches []' do
        expression = parser.parse('.send[message="[]"]')
        expect(expression.query_nodes(node)).to eq [node.body.last.body.third]
      end
    end
  end
end