func TestVisitor_AllowsForEditingOnEnter(t *testing.T) {

	query := `{ a, b, c { a, b, c } }`
	astDoc := parse(t, query)

	expectedQuery := `{ a,    c { a,    c } }`
	expectedAST := parse(t, expectedQuery)
	v := &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:
				if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "b" {
					return visitor.ActionUpdate, nil
				}
			}
			return visitor.ActionNoChange, nil
		},
	}

	editedAst := visitor.Visit(astDoc, v, nil)
	if !reflect.DeepEqual(testutil.ASTToJSON(t, expectedAST), editedAst) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedAST, editedAst))
	}

}
func TestVisitor_AllowsANamedFunctionsVisitorAPI(t *testing.T) {

	query := `{ a, b { x }, c }`
	astDoc := parse(t, query)

	visited := []interface{}{}
	expectedVisited := []interface{}{
		[]interface{}{"enter", "SelectionSet", nil},
		[]interface{}{"enter", "Name", "a"},
		[]interface{}{"enter", "Name", "b"},
		[]interface{}{"enter", "SelectionSet", nil},
		[]interface{}{"enter", "Name", "x"},
		[]interface{}{"leave", "SelectionSet", nil},
		[]interface{}{"enter", "Name", "c"},
		[]interface{}{"leave", "SelectionSet", nil},
	}

	v := &visitor.VisitorOptions{
		KindFuncMap: map[string]visitor.NamedVisitFuncs{
			"Name": visitor.NamedVisitFuncs{
				Kind: func(p visitor.VisitFuncParams) (string, interface{}) {
					switch node := p.Node.(type) {
					case map[string]interface{}:
						visited = append(visited, []interface{}{"enter", getMapValue(node, "Kind"), getMapValue(node, "Value")})
					}
					return visitor.ActionNoChange, nil
				},
			},
			"SelectionSet": visitor.NamedVisitFuncs{
				Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
					switch node := p.Node.(type) {
					case map[string]interface{}:
						visited = append(visited, []interface{}{"enter", getMapValue(node, "Kind"), getMapValue(node, "Value")})
					}
					return visitor.ActionNoChange, nil
				},
				Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
					switch node := p.Node.(type) {
					case map[string]interface{}:
						visited = append(visited, []interface{}{"leave", getMapValue(node, "Kind"), getMapValue(node, "Value")})
					}
					return visitor.ActionNoChange, nil
				},
			},
		},
	}

	_ = visitor.Visit(astDoc, v, nil)

	if !reflect.DeepEqual(visited, expectedVisited) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited))
	}
}
func Print(astNode ast.Node) (printed interface{}) {
	defer func() interface{} {
		if r := recover(); r != nil {
			return fmt.Sprintf("%v", astNode)
		}
		return printed
	}()
	printed = visitor.Visit(astNode, &visitor.VisitorOptions{
		LeaveKindMap: printDocASTReducer,
	}, nil)
	return printed
}
func TestVisitor_AllowsSkippingASubTree(t *testing.T) {

	query := `{ a, b { x }, c }`
	astDoc := parse(t, query)

	visited := []interface{}{}
	expectedVisited := []interface{}{
		[]interface{}{"enter", "Document", nil},
		[]interface{}{"enter", "OperationDefinition", nil},
		[]interface{}{"enter", "SelectionSet", nil},
		[]interface{}{"enter", "Field", nil},
		[]interface{}{"enter", "Name", "a"},
		[]interface{}{"leave", "Name", "a"},
		[]interface{}{"leave", "Field", nil},
		[]interface{}{"enter", "Field", nil},
		[]interface{}{"enter", "Field", nil},
		[]interface{}{"enter", "Name", "c"},
		[]interface{}{"leave", "Name", "c"},
		[]interface{}{"leave", "Field", nil},
		[]interface{}{"leave", "SelectionSet", nil},
		[]interface{}{"leave", "OperationDefinition", nil},
		[]interface{}{"leave", "Document", nil},
	}

	v := &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:
				visited = append(visited, []interface{}{"enter", getMapValue(node, "Kind"), getMapValue(node, "Value")})
				if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "b" {
					return visitor.ActionSkip, nil
				}
			}
			return visitor.ActionNoChange, nil
		},
		Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:
				visited = append(visited, []interface{}{"leave", getMapValue(node, "Kind"), getMapValue(node, "Value")})
			}
			return visitor.ActionNoChange, nil
		},
	}

	_ = visitor.Visit(astDoc, v, nil)

	if !reflect.DeepEqual(visited, expectedVisited) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited))
	}
}
func TestVisitor_ProducesHelpfulErrorMessages(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			err := r.(string)
			expectedErr := `Invalid AST Node (4): map[random:Data]`
			if err != expectedErr {
				t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(err, expectedErr))
			}
			return
		}
		t.Fatalf("expected to panic")
	}()
	astDoc := map[string]interface{}{
		"random": "Data",
	}
	_ = visitor.Visit(astDoc, nil, nil)
}
func TestVisitor_VisitsEditedNode(t *testing.T) {

	query := `{ a { x } }`
	astDoc := parse(t, query)

	addedField := map[string]interface{}{
		"Kind": "Field",
		"Name": map[string]interface{}{
			"Kind":  "Name",
			"Value": "__typename",
		},
	}

	didVisitAddedField := false
	v := &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:
				if getMapValueString(node, "Kind") == "Field" && getMapValueString(node, "Name.Value") == "a" {
					s := getMapValue(node, "SelectionSet.Selections").([]interface{})
					s = append(s, addedField)
					return visitor.ActionUpdate, map[string]interface{}{
						"Kind":         "Field",
						"SelectionSet": s,
					}
				}
				if reflect.DeepEqual(node, addedField) {
					didVisitAddedField = true
				}
			}
			return visitor.ActionNoChange, nil
		},
	}

	_ = visitor.Visit(astDoc, v, nil)
	if didVisitAddedField == false {
		t.Fatalf("Unexpected result, expected didVisitAddedField == true")
	}
}
func TestVisitor_VisitsKitchenSink(t *testing.T) {
	b, err := ioutil.ReadFile("./../parser/kitchen-sink.graphql")
	if err != nil {
		t.Fatalf("unable to load kitchen-sink.graphql")
	}

	query := string(b)
	astDoc := parse(t, query)

	visited := []interface{}{}
	expectedVisited := []interface{}{
		[]interface{}{"enter", "Document", nil, nil},
		[]interface{}{"enter", "OperationDefinition", 0, nil},
		[]interface{}{"enter", "Name", "Name", "OperationDefinition"},
		[]interface{}{"leave", "Name", "Name", "OperationDefinition"},
		[]interface{}{"enter", "VariableDefinition", 0, nil},
		[]interface{}{"enter", "Variable", "Variable", "VariableDefinition"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Variable", "VariableDefinition"},
		[]interface{}{"enter", "NamedType", "Type", "VariableDefinition"},
		[]interface{}{"enter", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "NamedType", "Type", "VariableDefinition"},
		[]interface{}{"leave", "VariableDefinition", 0, nil},
		[]interface{}{"enter", "VariableDefinition", 1, nil},
		[]interface{}{"enter", "Variable", "Variable", "VariableDefinition"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Variable", "VariableDefinition"},
		[]interface{}{"enter", "NamedType", "Type", "VariableDefinition"},
		[]interface{}{"enter", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "NamedType", "Type", "VariableDefinition"},
		[]interface{}{"enter", "EnumValue", "DefaultValue", "VariableDefinition"},
		[]interface{}{"leave", "EnumValue", "DefaultValue", "VariableDefinition"},
		[]interface{}{"leave", "VariableDefinition", 1, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Alias", "Field"},
		[]interface{}{"leave", "Name", "Alias", "Field"},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "ListValue", "Value", "Argument"},
		[]interface{}{"enter", "IntValue", 0, nil},
		[]interface{}{"leave", "IntValue", 0, nil},
		[]interface{}{"enter", "IntValue", 1, nil},
		[]interface{}{"leave", "IntValue", 1, nil},
		[]interface{}{"leave", "ListValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"enter", "InlineFragment", 1, nil},
		[]interface{}{"enter", "NamedType", "TypeCondition", "InlineFragment"},
		[]interface{}{"enter", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "NamedType", "TypeCondition", "InlineFragment"},
		[]interface{}{"enter", "Directive", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Directive"},
		[]interface{}{"leave", "Name", "Name", "Directive"},
		[]interface{}{"leave", "Directive", 0, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "InlineFragment"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"enter", "Field", 1, nil},
		[]interface{}{"enter", "Name", "Alias", "Field"},
		[]interface{}{"leave", "Name", "Alias", "Field"},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "IntValue", "Value", "Argument"},
		[]interface{}{"leave", "IntValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"enter", "Argument", 1, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "Variable", "Value", "Argument"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 1, nil},
		[]interface{}{"enter", "Directive", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Directive"},
		[]interface{}{"leave", "Name", "Name", "Directive"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "Variable", "Value", "Argument"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"leave", "Directive", 0, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"enter", "FragmentSpread", 1, nil},
		[]interface{}{"enter", "Name", "Name", "FragmentSpread"},
		[]interface{}{"leave", "Name", "Name", "FragmentSpread"},
		[]interface{}{"leave", "FragmentSpread", 1, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"leave", "Field", 1, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "InlineFragment"},
		[]interface{}{"leave", "InlineFragment", 1, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"leave", "OperationDefinition", 0, nil},
		[]interface{}{"enter", "OperationDefinition", 1, nil},
		[]interface{}{"enter", "Name", "Name", "OperationDefinition"},
		[]interface{}{"leave", "Name", "Name", "OperationDefinition"},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "IntValue", "Value", "Argument"},
		[]interface{}{"leave", "IntValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"enter", "Directive", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Directive"},
		[]interface{}{"leave", "Name", "Name", "Directive"},
		[]interface{}{"leave", "Directive", 0, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "Field"},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"leave", "OperationDefinition", 1, nil},
		[]interface{}{"enter", "FragmentDefinition", 2, nil},
		[]interface{}{"enter", "Name", "Name", "FragmentDefinition"},
		[]interface{}{"leave", "Name", "Name", "FragmentDefinition"},
		[]interface{}{"enter", "NamedType", "TypeCondition", "FragmentDefinition"},
		[]interface{}{"enter", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "Name", "Name", "NamedType"},
		[]interface{}{"leave", "NamedType", "TypeCondition", "FragmentDefinition"},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "FragmentDefinition"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "Variable", "Value", "Argument"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"enter", "Argument", 1, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "Variable", "Value", "Argument"},
		[]interface{}{"enter", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Name", "Name", "Variable"},
		[]interface{}{"leave", "Variable", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 1, nil},
		[]interface{}{"enter", "Argument", 2, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "ObjectValue", "Value", "Argument"},
		[]interface{}{"enter", "ObjectField", 0, nil},
		[]interface{}{"enter", "Name", "Name", "ObjectField"},
		[]interface{}{"leave", "Name", "Name", "ObjectField"},
		[]interface{}{"enter", "StringValue", "Value", "ObjectField"},
		[]interface{}{"leave", "StringValue", "Value", "ObjectField"},
		[]interface{}{"leave", "ObjectField", 0, nil},
		[]interface{}{"leave", "ObjectValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 2, nil},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "FragmentDefinition"},
		[]interface{}{"leave", "FragmentDefinition", 2, nil},
		[]interface{}{"enter", "OperationDefinition", 3, nil},
		[]interface{}{"enter", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"enter", "Field", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"enter", "Argument", 0, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "BooleanValue", "Value", "Argument"},
		[]interface{}{"leave", "BooleanValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 0, nil},
		[]interface{}{"enter", "Argument", 1, nil},
		[]interface{}{"enter", "Name", "Name", "Argument"},
		[]interface{}{"leave", "Name", "Name", "Argument"},
		[]interface{}{"enter", "BooleanValue", "Value", "Argument"},
		[]interface{}{"leave", "BooleanValue", "Value", "Argument"},
		[]interface{}{"leave", "Argument", 1, nil},
		[]interface{}{"leave", "Field", 0, nil},
		[]interface{}{"enter", "Field", 1, nil},
		[]interface{}{"enter", "Name", "Name", "Field"},
		[]interface{}{"leave", "Name", "Name", "Field"},
		[]interface{}{"leave", "Field", 1, nil},
		[]interface{}{"leave", "SelectionSet", "SelectionSet", "OperationDefinition"},
		[]interface{}{"leave", "OperationDefinition", 3, nil},
		[]interface{}{"leave", "Document", nil, nil},
	}

	v := &visitor.VisitorOptions{
		Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:

				parent, ok := p.Parent.(map[string]interface{})
				if !ok {
					parent = map[string]interface{}{}
				}

				visited = append(visited, []interface{}{"enter", getMapValue(node, "Kind"), p.Key, getMapValue(parent, "Kind")})
			}
			return visitor.ActionNoChange, nil
		},
		Leave: func(p visitor.VisitFuncParams) (string, interface{}) {
			switch node := p.Node.(type) {
			case map[string]interface{}:

				parent, ok := p.Parent.(map[string]interface{})
				if !ok {
					parent = map[string]interface{}{}
				}

				visited = append(visited, []interface{}{"leave", getMapValue(node, "Kind"), p.Key, getMapValue(parent, "Kind")})
			}
			return visitor.ActionNoChange, nil
		},
	}

	_ = visitor.Visit(astDoc, v, nil)

	if !reflect.DeepEqual(visited, expectedVisited) {
		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedVisited, visited))
	}
}