0

I'm trying to automate a few things around the office and this program is supposed to log into our insurance companies website and pull information for use by payroll. Here is the function I use to input values in the webpage.

Public Function SetElement(Doc As HTMLDocument, MType As String, SelState As String, _
vInPut As Variant, Optional aIndex As Long = 0, Optional milSec As Long = 0) As Long
SetElement = 0
Sleep milSec
On Error GoTo Failed
Select Case MType
    Case "I": Doc.getElementById(SelState).Value = vInPut: SetElement = 1
    Case "N": Doc.getElementsByName(SelState)(aIndex).Value = vInPut: SetElement = 1
    Case "C": Doc.getElementsByClassName(SelState)(aIndex).Value = vInPut: SetElement = 1
    Case "T": Doc.getElementsByTagName(SelState)(aIndex).Value = vInPut: SetElement = 1
    Case "Q": Doc.querySelector(SelState).Value = vInPut: SetElement = 1
End Select
Failed:
End Function

Here is the function call as it appears in the main sub

i = 0: Do While i = 0
    i = SetElement(Doc, "N", "loginEmail", lcLogin, 0, 500)
Loop

The correct username get put into the text box on the webpage, but when the code clicks the login button red text appears under the username and password asking me to put a username (same issue for the password box too).

Here is the website HTML for the input box

<input name="loginEmail" class="form-control gbx-unmasked ng-pristine ng-valid ng-touched" id="loginEmail" aria-label="Email" type="email" maxlength="64" placeholder="" value="" data-dl='{"event":"change","da_track":"true","interaction_only":"true"}' _ngcontent-qql-c18="" autocapitalize="off" autocomplete="off" autocorrect="off">

If I click the input box and type in a single character and delete it in either the username or password boxes the website behaves as if it now has the correct values. If i had to make an educated guess this has something to do with the "data-dl='{"event":"change","da_track":"true","interaction_only":"true"}" bit of code.

found a good HTML formatter so here is the entire code for the input box.

<div class="col-xs-12 no-padding" _ngcontent-upe-c11="">
<primary-form-text-input
    class="ng-touched ng-dirty ng-valid"
    _ngcontent-upe-c11=""
    CustomGridClass="email-field"
    formControlName="loginEmail"
    inputId="loginEmail"
    inputPlaceholder=""
    inputType="email"
    isAutoFocus="true"
    labelName="Email"
    maxlength="64"
    ngDefaultControl=""
    textType="text"
    _nghost-upe-c14=""
>
    <div class="row form-group has-feedback" _ngcontent-upe-c14="">
        <!---->
        <app-tertiary-page-label _ngcontent-upe-c14="" _nghost-upe-c17="">
            <div _ngcontent-upe-c17="">
                <label class="control-label" for="loginEmail" _ngcontent-upe-c17=""><em _ngcontent-upe-c17=""></em>Email </label>
            </div>
        </app-tertiary-page-label>
        <div class="email-field" _ngcontent-upe-c14="">
            <primary-text-input _ngcontent-upe-c14="" _nghost-upe-c18="">
                <div style="position: relative;" _ngcontent-upe-c18="">
                    <!---->
                    <input
                        name="loginEmail"
                        class="form-control gbx-unmasked ng-touched ng-dirty ng-valid"
                        id="loginEmail"
                        aria-label="Email"
                        type="email"
                        maxlength="64"
                        placeholder=""
                        value="test@temail.com"
                        data-dl='{"event":"change","da_track":"true","interaction_only":"true"}'
                        _ngcontent-upe-c18=""
                        autocapitalize="off"
                        autocomplete="off"
                        autocorrect="off"
                    />
                    <!----><!----><!---->
                    <span class="eye-span" hidden="" _ngcontent-upe-c18="">
                        <!---->
                        <em
                            class="icon icon-view icon-2x"
                            ondragstart="return false"
                            ondrop="return false"
                            data-dl='{"event":"mousedown","da_track":"true","event_type":"Link Click","event_id":"Show Password"}'
                            _ngcontent-upe-c18=""
                            alt="Show password"
                        ></em>
                        <!---->
                    </span>
                </div>
            </primary-text-input>
        </div>
        <div class="col-xs-12 no-padding gotham-book" _ngcontent-upe-c14="">
            <!---->
            <span class="icon icon-attention form-control-feedback" aria-hidden="true" _ngcontent-upe-c14=""></span>
            <!---->
        </div>
    </div>
</primary-form-text-input>

UPDATE: I think I found the jscript function

                function ar(e) {
                return r["ɵvid"](0, [(e()(), r["ɵeld"](0, 0, [[1, 0], ["name", 1]], null, 6, "input", [["autocapitalize", "off"], ["autocomplete", "off"], ["autocorrect", "off"], ["data-dl", '{"event":"change","da_track":"true","interaction_only":"true"}']], [[8, "type", 0], [8, "placeholder", 0], [8, "id", 0], [8, "name", 0], [1, "maxlength", 0], [1, "minlength", 0], [1, "max", 0], [8, "className", 0], [1, "aria-label", 0], [1, "value", 0], [8, "readOnly", 0], [2, "ng-untouched", null], [2, "ng-touched", null], [2, "ng-pristine", null], [2, "ng-dirty", null], [2, "ng-valid", null], [2, "ng-invalid", null], [2, "ng-pending", null]], [[null, "blur"], [null, "focus"], [null, "input"], [null, "compositionstart"], [null, "compositionend"]], (function (e, t, n) {
                                    var i = !0,
                                    o = e.component;
                                    return "input" === t && (i = !1 !== r["ɵnov"](e, 1)._handleInput(n.target.value) && i),
                                    "blur" === t && (i = !1 !== r["ɵnov"](e, 1).onTouched() && i),
                                    "compositionstart" === t && (i = !1 !== r["ɵnov"](e, 1)._compositionStart() && i),
                                    "compositionend" === t && (i = !1 !== r["ɵnov"](e, 1)._compositionEnd(n.target.value) && i),
                                    "blur" === t && (i = !1 !== o.onChange(n, r["ɵnov"](e, 0).value) && i),
                                    "focus" === t && (i = !1 !== o.onFocusFunction() && i),
                                    i
                                }), null, null)), r["ɵdid"](1, 16384, null, 0, Jn.DefaultValueAccessor, [r.Renderer2, r.ElementRef, [2, Jn.COMPOSITION_BUFFER_MODE]], null, null), r["ɵprd"](1024, null, Jn.NG_VALUE_ACCESSOR, (function (e) {
                                return [e]
                            }), [Jn.DefaultValueAccessor]), r["ɵdid"](3, 540672, null, 0, Jn.FormControlDirective, [[8, null], [8, null], [6, Jn.NG_VALUE_ACCESSOR], [2, Jn["ɵangular_packages_forms_forms_q"]]], {
                            form: [0, "form"]
                        }, null), r["ɵprd"](2048, null, Jn.NgControl, null, [Jn.FormControlDirective]), r["ɵdid"](5, 16384, null, 0, Jn.NgControlStatus, [[4, Jn.NgControl]], null, null), r["ɵdid"](6, 4210688, null, 0, Kn, [Qn, er, r.ElementRef, $n], null, null)], (function (e, t) {
                        e(t, 3, 0, t.component.controls)
                    }), (function (e, t) {
                        var n = t.component;
                        e(t, 0, 1, [n.inputType, n.inputPlaceholder, n.inputId, n.inputId, n.maxlength, n.minlength, n.max, r["ɵinlineInterpolate"](7, "form-control ", "password" === n.inputType ? "set-password-eye-align" : "", " ", "loginPassword" === n.inputId ? "login-password-star" : "", " ", "email" === n.inputId || "securityCode" === n.inputId ? "gbx-unmasked" : "", "", "emailControl" === n.inputId ? "gbx-unmasked" : "", "", "loginEmail" === n.inputId ? "gbx-unmasked" : "", "", "firstName" === n.inputId ? "gbx-unmasked" : "", "", "lastName" === n.inputId ? "gbx-unmasked" : "", ""), n.labelName, n.inputValue, n.readOnlyCondition, r["ɵnov"](t, 5).ngClassUntouched, r["ɵnov"](t, 5).ngClassTouched, r["ɵnov"](t, 5).ngClassPristine, r["ɵnov"](t, 5).ngClassDirty, r["ɵnov"](t, 5).ngClassValid, r["ɵnov"](t, 5).ngClassInvalid, r["ɵnov"](t, 5).ngClassPending])
                    }))
            }

How do I call it?

I just remembered that this is a public website.

Here is the link.

  • 2
    1) Possibly a pause is needed before/after text entry 2) there is an event that needs to be triggered -> right click inspect element > then view event listeners. If so, you may need to attach/fire them 3) you may simply need to change the class name value -> check what it is after manual text entry then use setAttribute to update the class, in the code, after text entry 4) Examine the html/source files to see what the definition of `da_track` is, then work out whether you can use execScript to call it. – QHarr May 16 '21 at 01:06
  • I checked the class name for the input box before and after a manual entry and you're right. It does change. Before: "form-control gbx-unmasked ng-pristine ng-valid ng-touched" After: "form-control gbx-unmasked ng-touched ng-dirty ng-invalid" Strangely enough the "test@temail.com" that my code enters as a dummy value shows as text on screen but does not appear in the value area of the html (or anywhere in the entire HTML document) until I manually enter a character with a keystroke. Any idea what could be happening? – Rational Redneck May 16 '21 at 08:05
  • 1
    Like QHarr wrote, the input fields have events you must trigger to make the username and passwort work for the page. First trigger `compositionstart`, then enter username and then trigger `compositionend`. Same for the password. It may also be that it is enough to trigger the `change` event after you have filled a text field. Here you can see what to do in practice: https://stackoverflow.com/questions/63294113/automate-ie-via-excel-to-fill-in-a-dropdown-and-continue/63299608#63299608 – Zwenn May 16 '21 at 11:55

1 Answers1

0

I got it working with the help of Zwenn's old answer. Here is the solution I came up with after adapting his code.

Sub login
Dim IE As Object
Dim doc As HTMLDocument
Dim i As Long
Dim aLogin As String
Dim aPsw As String

'Set the login details here
aLogin = "*****"
aPsw = "*****"

'Create a new instance of IE
Set IE = CreateObject("InternetExplorer.application")
'Set to true if you want to watch whats happening and false if you want it to run in the background
IE.Visible = True

'navigate to the website
IE.navigate "https://account.thehartford.com/customer/#/login?appid=EE"

'wait for the website to load
Do While IE.Busy: DoEvents: Loop

'Shorten IE.document to doc, because I'm lazy
Set doc = IE.document

'I've found wrapping interactions with websites into their own generic functions with error handling
'to be the most reliable method of web automation.  Someone please tell me if this approach
'has a drawback I'm not aware of.

'Here we trigger the comstart event, input values and trigger compend; for bother the user names and password.
'Then we click the login button.  The case statement ensures everything happens in the correct order.
i = 0
Do While i <= 6
    Select Case i
        Case 0: i = i + TriggerEvent(doc, "I", "loginPassword", "compositionstart", 0, 200)
        Case 1: i = i + SetElement(doc, "I", "loginEmail", aLogin, 0, 100)
        Case 2: i = i + TriggerEvent(doc, "I", "loginEmail", "compositionend", 0, 100)
        Case 3: i = i + TriggerEvent(doc, "I", "loginEmail", "compositionstart", 0, 200)
        Case 4: i = i + SetElement(doc, "I", "loginPassword", aPsw, 0, 100)
        Case 5: i = i + TriggerEvent(doc, "I", "loginPassword", "compositionend", 0, 100)
        Case 6: i = i + ClickElement(doc, "I", "btNext", 0, 200)
    End Select
Loop
End Sub

Here is the function that triggers the event.

'A function to trigger an event on a webpage. MType specifies which method you use to identify the element on the webpage,
'SelStatement specifies the text that identifies the element, Event type tells the function which event to trigger
Public Function TriggerEvent(doc As HTMLDocument, MType As String, SelState As String, eventType As String, Optional aIndex As Long = 0, Optional milSec As Long = 100) As Long
    Dim theEvent As Object
    Dim htmlElementWithEvent As Object
    Sleep millSec
    TriggerEvent = 0
    On Error GoTo Failed
    Select Case MType
        Case "I": Set htmlElementWithEvent = doc.getElementById(SelState): TriggerEvent = 1
        Case "N": Set htmlElementWithEvent = doc.getElementsByName(SelState)(aIndex): TriggerEvent = 1
        Case "C": Set htmlElementWithEvent = doc.getElementsByClassName(SelState)(aIndex): TriggerEvent = 1
        Case "T": Set htmlElementWithEvent = doc.getElementsByTagName(SelState)(aIndex): TriggerEvent = 1
        Case "Q": Set htmlElementWithEvent = doc.querySelector(SelState): TriggerEvent = 1
    End Select
    htmlElementWithEvent.Focus
    Set theEvent = doc.createEvent("HTMLEvents")
    theEvent.initEvent eventType, True, False
    htmlElementWithEvent.dispatchEvent theEvent
Failed:
End Function

And for completeness sake here is the click function.

'Adds built in error handling and delay to common click methods.  Requires declaring 
the sleep function from kernel32 lib
Public Function ClickElement(doc As HTMLDocument, MType As String, SelState As String, 
Optional aIndex As Long = 0, Optional milSec As Long = 0) As Long
'Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
ClickElement = 0
Sleep milSec
On Error GoTo Failed
Select Case MType
    Case "I": doc.getElementById(SelState).Click: ClickElement = 1
    Case "N": doc.getElementsByName(SelState)(aIndex).Click: ClickElement = 1
    Case "C": doc.getElementsByClassName(SelState)(aIndex).Click: ClickElement = 1
    Case "T": doc.getElementsByTagName(SelState)(aIndex).Click: ClickElement = 1
    Case "Q": doc.querySelector(SelState).Click: ClickElement = 1
End Select
Failed:
End Function