Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AutoFillFormSuggestion for select,textarea in form #921

Merged
merged 2 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions pkg/engine/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,16 +527,16 @@ func bodyFormTagParser(resp *navigation.Response) (navigationRequests []*navigat
multipartWriter = multipart.NewWriter(&sb)
}

// Get the form field suggestions for all inputs
formInputs := []utils.FormInput{}
item.Find("input").Each(func(index int, item *goquery.Selection) {
// Get the form field suggestions for all elements in the form
formFields := []interface{}{}
item.Find("input, select, textarea").Each(func(index int, item *goquery.Selection) {
if len(item.Nodes) == 0 {
return
}
formInputs = append(formInputs, utils.ConvertGoquerySelectionToFormInput(item))
formFields = append(formFields, utils.ConvertGoquerySelectionToFormField(item))
})

dataMap := utils.FormInputFillSuggestions(formInputs)
dataMap := utils.FormFillSuggestions(formFields)
dataMap.Iterate(func(key, value string) bool {
if key == "" {
return true
Expand Down
169 changes: 169 additions & 0 deletions pkg/utils/formfill.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ type FormInput struct {
Attributes mapsutil.OrderedMap[string, string]
}

// FormOption is an option for a select input
type FormOption struct {
dogancanbakir marked this conversation as resolved.
Show resolved Hide resolved
Value string
Selected string
Attributes mapsutil.OrderedMap[string, string]
}

// FormSelect is a select input for a form field
type FormSelect struct {
Name string
Attributes mapsutil.OrderedMap[string, string]
FormOptions []FormOption
}

type FormTextArea struct {
Name string
Attributes mapsutil.OrderedMap[string, string]
}

// FormInputFillSuggestions returns a list of form filling suggestions
// for inputs returning the specified recommended values.
func FormInputFillSuggestions(inputs []FormInput) mapsutil.OrderedMap[string, string] {
Expand Down Expand Up @@ -106,6 +125,78 @@ func FormInputFillSuggestions(inputs []FormInput) mapsutil.OrderedMap[string, st
return data
}

// FormSelectFill fills a map with selected values from a slice of FormSelect structs.
// It iterates over each FormSelect struct in the inputs slice and checks for a selected option.
// If a selected option is found, it adds the corresponding value to the map using the input's name as the key.
// If no option is selected, it selects the first option and adds its value to the map.
// The function returns the filled map.
func FormSelectFill(inputs []FormSelect) mapsutil.OrderedMap[string, string] {
data := mapsutil.NewOrderedMap[string, string]()
for _, input := range inputs {
for _, option := range input.FormOptions {
if option.Selected != "" {
data.Set(input.Name, option.Value)
break
}
}

// If no option is selected, select the first one
if !data.Has(input.Name) && len(input.FormOptions) > 0 {
data.Set(input.Name, input.FormOptions[0].Value)
}
}
return data
}

// FormTextAreaFill fills the form text areas with placeholder values.
// It takes a slice of FormTextArea structs as input and returns an OrderedMap
// containing the form field names as keys and the placeholder values as values.
func FormTextAreaFill(inputs []FormTextArea) mapsutil.OrderedMap[string, string] {
data := mapsutil.NewOrderedMap[string, string]()
for _, input := range inputs {
data.Set(input.Name, FormData.Placeholder)
}
return data
}

// FormFillSuggestions takes a slice of form fields and returns an ordered map
// containing suggestions for filling those form fields. The function iterates
// over each form field and based on its type, calls the corresponding fill
// function to generate suggestions. The suggestions are then merged into a
// single ordered map and returned.
//
// Parameters:
// - formFields: A slice of form fields.
//
// Returns:
// An ordered map containing suggestions for filling the form fields.
func FormFillSuggestions(formFields []interface{}) mapsutil.OrderedMap[string, string] {
merged := mapsutil.NewOrderedMap[string, string]()
for _, item := range formFields {
switch v := item.(type) {
case FormInput:
dataMapInputs := FormInputFillSuggestions([]FormInput{v})
dataMapInputs.Iterate(func(key, value string) bool {
dogancanbakir marked this conversation as resolved.
Show resolved Hide resolved
merged.Set(key, value)
return true
})
case FormSelect:
dataMapSelects := FormSelectFill([]FormSelect{v})
dataMapSelects.Iterate(func(key, value string) bool {
merged.Set(key, value)
return true
})
case FormTextArea:
dataMapTextArea := FormTextAreaFill([]FormTextArea{v})
dataMapTextArea.Iterate(func(key, value string) bool {
merged.Set(key, value)
return true
})
}
}
return merged
}

// ConvertGoquerySelectionToFormInput converts goquery selection to form input
func ConvertGoquerySelectionToFormInput(item *goquery.Selection) FormInput {
attrs := item.Nodes[0].Attr
Expand All @@ -125,3 +216,81 @@ func ConvertGoquerySelectionToFormInput(item *goquery.Selection) FormInput {
}
return input
}

// ConvertGoquerySelectionToFormOption converts a goquery.Selection object to a FormOption object.
// It extracts the attributes from the goquery.Selection object and populates a FormOption object with the extracted values.
func ConvertGoquerySelectionToFormOption(item *goquery.Selection) FormOption {
attrs := item.Nodes[0].Attr
input := FormOption{Attributes: mapsutil.NewOrderedMap[string, string]()}
for _, attribute := range attrs {
switch attribute.Key {
case "value":
input.Value = attribute.Val

case "selected":
input.Selected = attribute.Key
default:
input.Attributes.Set(attribute.Key, attribute.Val)
}
}
return input
}

// ConvertGoquerySelectionToFormSelect converts a goquery.Selection object to a FormSelect object.
// It extracts the attributes and form options from the goquery.Selection and populates them in the FormSelect object.
// The converted FormSelect object is then returned.
func ConvertGoquerySelectionToFormSelect(item *goquery.Selection) FormSelect {
attrs := item.Nodes[0].Attr
input := FormSelect{Attributes: mapsutil.NewOrderedMap[string, string]()}
for _, attribute := range attrs {
switch attribute.Key {
case "name":
input.Name = attribute.Val
default:
input.Attributes.Set(attribute.Key, attribute.Val)
}
}

input.FormOptions = []FormOption{}
item.Find("option").Each(func(_ int, option *goquery.Selection) {
input.FormOptions = append(input.FormOptions, ConvertGoquerySelectionToFormOption(option))
})
return input
}

// ConvertGoquerySelectionToFormTextArea converts a goquery.Selection object to a FormTextArea struct.
// It extracts the attributes from the first node of the selection and populates a FormTextArea object with the extracted data.
// The "name" attribute is assigned to the Name field of the FormTextArea, while other attributes are added to the Attributes map.
func ConvertGoquerySelectionToFormTextArea(item *goquery.Selection) FormTextArea {
attrs := item.Nodes[0].Attr
input := FormTextArea{Attributes: mapsutil.NewOrderedMap[string, string]()}
for _, attribute := range attrs {
switch attribute.Key {
case "name":
input.Name = attribute.Val
default:
input.Attributes.Set(attribute.Key, attribute.Val)
}
}
return input
}

// ConvertGoquerySelectionToFormField converts a goquery.Selection object to a form field.
// It checks the type of the selection and calls the appropriate conversion function.
// If the selection is an input, it calls ConvertGoquerySelectionToFormInput.
// If the selection is a select, it calls ConvertGoquerySelectionToFormSelect.
// If the selection is a textarea, it calls ConvertGoquerySelectionToFormTextArea.
// If the selection is of any other type, it returns nil.
func ConvertGoquerySelectionToFormField(item *goquery.Selection) interface{} {
if item.Is("input") {
return ConvertGoquerySelectionToFormInput(item)
}
if item.Is("select") {
return ConvertGoquerySelectionToFormSelect(item)
}
if item.Is("textarea") {
return ConvertGoquerySelectionToFormTextArea(item)
}

return nil
}
32 changes: 26 additions & 6 deletions pkg/utils/formfill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,27 @@ var htmlFormInputExample = `<html>
<input type="number" name="num" min="50" max="80">
<label><b>Enter your Telephone Number(in format of xxx-xxx-xxxx):</b></label>
<input type="tel" name="telephone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" required>
<br><br><input type="submit" value="submit">
<p>Kindly Select your favourite food</p>
<select name="food" id="food">
<option value="pizza">Pizza</option>
<option value="burger">Burger</option>
<option value="pasta" selected>Pasta</option>
</select>
<p>Kindly Select your favourite country</p>
<select name="country" id="country">
<option value="india">India</option>
<option value="usa">USA</option>
<option value="uk">UK</option>
<option value="canada">Canada</option>
</select>
<label><b>Write some words about yourself:</b></label>
<textarea id="message" name="message" rows="10" cols="50">
Write something here
</textarea>


<br><br><input type="submit" value="submit">

</form>
</body>
</html>`
Expand All @@ -44,16 +64,16 @@ func TestFormInputFillSuggestions(t *testing.T) {

document.Find("form[action]").Each(func(i int, item *goquery.Selection) {
queryValuesWriter := make(url.Values)
formInputs := []FormInput{}
formFields := []interface{}{}

item.Find("input").Each(func(index int, item *goquery.Selection) {
item.Find("input, textarea, select").Each(func(index int, item *goquery.Selection) {
if len(item.Nodes) == 0 {
return
}
formInputs = append(formInputs, ConvertGoquerySelectionToFormInput(item))
formFields = append(formFields, ConvertGoquerySelectionToFormField(item))
})

dataMap := FormInputFillSuggestions(formInputs)
dataMap := FormFillSuggestions(formFields)
dataMap.Iterate(func(key, value string) bool {
if key == "" || value == "" {
return true
Expand All @@ -62,6 +82,6 @@ func TestFormInputFillSuggestions(t *testing.T) {
return true
})
value := queryValuesWriter.Encode()
require.Equal(t, "Startdate=katana&color=red&firstname=katana&num=51&password=katana&sport1=cricket&sport2=tennis&sport3=football&telephone=katanaP%40assw0rd1&upclick=%23a52a2a", value, "could not get correct encoded form")
require.Equal(t, "Startdate=katana&color=green&country=india&firstname=katana&food=pasta&message=katana&num=51&password=katana&sport1=cricket&sport2=tennis&sport3=football&telephone=katanaP%40assw0rd1&upclick=%23a52a2a", value, "could not get correct encoded form")
})
}
Loading