0

I want to automate OTP submission on a bank page. I will get the OTP in my database only after webdriver have clicked confirm on the bank page. After confirming, I need to fetch OTP from the db and then automate OTP submission.

  ctx, cancel := chromedp.NewContext(context.Background(),      chromedp.WithDebugf(log.Printf))
    defer cancel()

    // run chromedp tasks
    err := chromedp.Run(ctx,
        chromedp.Navigate(bankUrl),
        chromedp.WaitVisible(`#username`),
        chromedp.SendKeys(`#username`, `usernameXXX`),
        chromedp.WaitVisible(`#label2`, ),
        chromedp.SendKeys(`#label2`, `passwordxxx` ),
        chromedp.Click(`//input[@title="Login"]`),
        chromedp.WaitVisible(`#Go`),
        chromedp.Click(`#Go`),
        chromedp.WaitVisible(`#confirmButton`),
        chromedp.Click(`#confirmButton`),
        chromedp.WaitVisible(`//input[@type="password"]`),
        // perform  fetch OTP below, this raise error
        otp := fetchOTPFromDb()
        chromedp.SendKeys(`//input[@type="password"]`, otp),
        chromedp.WaitVisible(`#confirmButton`),
        chromedp.Click(`#confirmButton`))
    if err != nil {
        log.Fatal(err)
    }

The problem is that chromedp.Run expects all args to be of the type chromedp.Tasks, so I can't call custom functions there and I get error while fetching the OTP from db. How do I go around this?

Umesh Malhotra
  • 967
  • 1
  • 10
  • 25
  • OTP for One Time Password ? I guess, you want to get that OTP then inject it into the webpage before submitting it ? –  Jul 26 '21 at 18:01
  • Yes @mh-cbon. The issue is that I receive OTP only after half of the steps in automation completed. So I would have to deviate from chromedp.Tasks and call some internal function to fetch OTP. – Umesh Malhotra Jul 26 '21 at 18:08
  • I guess it is not possible to put that fetch before the call to chromedp.Run because the data does not exists yet ? –  Jul 26 '21 at 18:14
  • see the documentation of chromedp.Run, at https://pkg.go.dev/github.com/chromedp/chromedp#Run it takes a slice of chromedp.Action. https://pkg.go.dev/github.com/chromedp/chromedp#Action So you could write a new Action that implements the Do method, fetch the OTP on the db, then return the value of a call to `Send(sel, value)` https://github.com/chromedp/chromedp/blob/v0.7.4/query.go#L1023 –  Jul 26 '21 at 18:21
  • Yes @mh-cbon, we will get OTP only after chromedp have logged-in to the page and clicked "confirm" button. So, its not possible to fetch the OTP before chromedp.Run – Umesh Malhotra Jul 26 '21 at 18:21
  • Thanks @mh-cbon, trying actionFunc. Any idea why below code is not working? It should print `got: otpFromDb` whereas, its printing `got: `. It means otp is not getting set. Code link - https://ideone.com/167Ovg – Umesh Malhotra Jul 27 '21 at 11:51
  • 1
    what if you try https://play.golang.org/p/_oNQfWHwTxz ? –  Jul 27 '21 at 12:00

1 Answers1

1

The solution is to wrap the otp fetch within an Action.Do call, then return the result of a call to chromdp.SendKeys to set the HTML input value.

It is required to work that way because the One Time Password does not exist until the page was fetched, thus, its read must happen while manipulating the resource.

Like this

package main

import "context"

type OTPAction struct {
    // DB ....
}

func (a OTPAction) Do(ctx context.Context) error {
    // fetch OTP here
    otp := "otp test"
    return chromedp.SendKeys(`//input[@id="user-message"]`, otp).Do(ctx)
}
Umesh Malhotra
  • 967
  • 1
  • 10
  • 25