diff --git a/goSpider.go b/goSpider.go index b10735b..1fc123d 100644 --- a/goSpider.go +++ b/goSpider.go @@ -14,6 +14,8 @@ import ( "io/ioutil" "log" "os" + "regexp" + "strconv" "strings" "sync" "time" @@ -1020,6 +1022,120 @@ func (nav *Navigator) MakeElementVisible(selector string) error { return nil } +// Datepicker deals with date-picker elements on websites by receiving a date, calculates the amount of time it needs to go back in the picker and finally selects a day. +// +// date: string in the format "dd/mm/aaaa" +// calendarButtonSelector: the css selector of the data-picker +// calendarButtonGoBack: the css selector of the go back button +// calendarButtonsTableXpath: the xpath of the days table example: "//*[@id="ui-datepicker-div"]/table/tbody/tr"; +// calendarButtonTR: the css selector of the days table row, example: "//*[@id="ui-datepicker-div"]/table/tbody/tr" +func (nav *Navigator) Datepicker(date, calendarButtonSelector, calendarButtonGoBack, calendarButtonsTableXpath, calendarButtonTR string) error { + regex := `^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/[0-9]{4}$` + r := regexp.MustCompile(regex) + if r.MatchString(date) == false { + return errors.New("date does not match with dd/mm/aaaa") + } + + parsedDate, err := time.Parse("02/01/2006", date) + if err != nil { + return errors.New("error parsing date: " + err.Error()) + } + + today := time.Now().Format("02/01/2006") + parseToday, err := time.Parse("02/01/2006", today) + if err != nil { + return errors.New("error parsing today's date: " + err.Error()) + } + + // Ensure startDate is before endDate + if parsedDate.After(parseToday) { + return errors.New("date must be older then today") + } + years, months, _ := calculateDateDifference(parsedDate, parseToday) + + err = nav.ClickButton(calendarButtonSelector) + if err != nil { + return err + } + + i := 0 + for { + err = chromedp.Run(nav.Ctx, chromedp.Click(calendarButtonGoBack)) + if err != nil { + break + } + i++ + if i == ((years * 12) + months) { + break + } + } + + err = nav.WaitForElement(calendarButtonsTableXpath, time.Minute) + if err != nil { + return err + } + + pageSource, err := nav.GetPageSource() + if err != nil { + return err + } + + tt, err := htmlquery.Find(pageSource, calendarButtonsTableXpath) + if err != nil { + return err + } + + for k, node := range tt { + for l := 1; l < 8; l++ { + day, err := ExtractText(node, "td["+strconv.Itoa(l)+"]", "") + if err != nil { + return err + } + if day == strconv.Itoa(parsedDate.Day()) { + err = nav.ClickButton(calendarButtonsTableXpath + "[" + strconv.Itoa(k+1) + "]/td[" + strconv.Itoa(l) + "]") + if err != nil { + return errors.New("error clicking button on calendar button: " + calendarButtonTR + "(" + strconv.Itoa(k) + ") > td:nth-child(" + strconv.Itoa(l) + "). Error code: " + err.Error()) + } else { + return nil + } + } + + } + + } + return errors.New("could not pick date") +} +func calculateDateDifference(startDate, endDate time.Time) (int, int, int) { + years := endDate.Year() - startDate.Year() + months := int(endDate.Month()) - int(startDate.Month()) + days := endDate.Day() - startDate.Day() + + // Adjust the difference if necessary + if days < 0 { + // Borrow days from the previous month + months-- + previousMonth := endDate.AddDate(0, -1, 0) + days += previousMonth.Day() + } + + if months < 0 { + // Borrow months from the previous year + years-- + months += 12 + } + + return years, months, days +} + +func PrintHtml(pageSource *html.Node) (string, error) { + var sb strings.Builder + err := html.Render(&sb, pageSource) + if err != nil { + return "", err + } + return sb.String(), nil +} + // Close closes the Navigator instance and releases resources. // Example: // diff --git a/goSpider_test.go b/goSpider_test.go index ebb8891..c45fca8 100644 --- a/goSpider_test.go +++ b/goSpider_test.go @@ -605,6 +605,67 @@ func TestMakeElementVisible(t *testing.T) { } } +func TestPrintHtml(t *testing.T) { + server := startTestServer() + defer server.Close() + + nav := setupNavigator(t) + defer nav.Close() + + err := nav.OpenURL(server.URL + "/test.html") + if err != nil { + t.Errorf("OpenURL error: %v", err) + } + + ps, err := nav.GetPageSource() + if err != nil { + t.Errorf("GetPageSource error: %v", err) + } + + s, err := PrintHtml(ps) + if err != nil { + t.Errorf("PrintHtml error: %v", err) + } + + fmt.Println(s) + +} + +func TestDatepicker(t *testing.T) { + nav := NewNavigator("", false) + + err := nav.OpenURL("https://www.tjrs.jus.br/buscas/jurisprudencia/?conteudo_busca=ementa_completa&q_palavra_chave=&aba=jurisprudencia&q=&conteudo_busca=ementa_completa") + if err != nil { + t.Errorf("OpenURL error: %v", err) + return + } + + err = nav.WaitForElement("#datas_julgamento_publicacao_div > div:nth-child(2) > div.col-md-4.col-xs-12.dataPublicacaoContainer > div > div > div.recuo_esquerdo_10px.col-md-5.col-xs-5 > div > input", time.Minute) + if err != nil { + t.Errorf("WaitForElement error: %v", err) + return + } + + err = nav.Datepicker("01/01/2000", "#datas_julgamento_publicacao_div > div:nth-child(2) > div.col-md-4.col-xs-12.dataPublicacaoContainer > div > div > div.recuo_esquerdo_10px.col-md-5.col-xs-5 > div > span > i", "#ui-datepicker-div > div > a.ui-datepicker-prev.ui-corner-all > span", "//*[@id=\"ui-datepicker-div\"]/table/tbody/tr", "#ui-datepicker-div > table > tbody > tr:nth-child") + if err != nil { + t.Errorf("Datepicker error: %v", err) + return + } + + err = nav.Datepicker("31/12/2000", "#datas_julgamento_publicacao_div > div:nth-child(2) > div.col-md-4.col-xs-12.dataPublicacaoContainer > div > div > div:nth-child(3) > div > span", "#ui-datepicker-div > div > a.ui-datepicker-prev.ui-corner-all > span", "//*[@id=\"ui-datepicker-div\"]/table/tbody/tr", "#ui-datepicker-div > table > tbody > tr:nth-child") + if err != nil { + t.Errorf("Datepicker error: %v", err) + return + } + + start := time.Now() + for { + if time.Since(start) > time.Minute { + break + } + } +} + // Won't pass on test because 2FA requires input on the terminal by the user, for that reason alone the test will fail //// TestLoginGoogle tests google single logon //func TestLoginGoogle(t *testing.T) {