Commit 34a6f41d authored by Yiming Zhou's avatar Yiming Zhou Committed by Commit Bot

Add browser tests for autofilling the shipping address info and payment info on captured sites.

These tests are the first step in building a test framework for testing chrome autofill on complex, real-world sites pages. Since interacting with live sites are flaky and vulnerable to updates to the sites' features, this framework interacts with captured sites.

Sites are captured with the Web Page Replay (WPR) tool. Prior to running the test, the browser test will start WPR locally on the machine using a capture file. The test then navigates to the test site, interact with the site's checkout workflow, and verifies that autofill correctly completes the address and payment information.

Tests for 5 sites are included in this code change: Amazon, Zappos, Apple, Ebay and Walmart.

Change-Id: I4b28e645554a2912c632fcb462439cac7142cf9f
Reviewed-on: https://chromium-review.googlesource.com/963800
Commit-Queue: Yiming Zhou <uwyiming@google.com>
Reviewed-by: default avataranthonyvd <anthonyvd@chromium.org>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551190}
parent 855ba32b
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/autofill/autofill_uitest.h"
#include "chrome/browser/autofill/autofill_uitest_util.h" #include "chrome/browser/autofill/autofill_uitest_util.h"
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
...@@ -163,68 +164,6 @@ static const char kTestEventFormString[] = ...@@ -163,68 +164,6 @@ static const char kTestEventFormString[] =
" <input type=\"text\" id=\"phone\"><br>" " <input type=\"text\" id=\"phone\"><br>"
"</form>"; "</form>";
// AutofillManagerTestDelegateImpl --------------------------------------------
class AutofillManagerTestDelegateImpl
: public autofill::AutofillManagerTestDelegate {
public:
AutofillManagerTestDelegateImpl() {}
~AutofillManagerTestDelegateImpl() override {}
// autofill::AutofillManagerTestDelegate:
void DidPreviewFormData() override {
ASSERT_TRUE(loop_runner_ != nullptr);
ASSERT_TRUE(loop_runner_->loop_running());
loop_runner_->Quit();
}
void DidFillFormData() override {
ASSERT_TRUE(loop_runner_ != nullptr);
if (!is_expecting_dynamic_refill_)
ASSERT_TRUE(loop_runner_->loop_running());
loop_runner_->Quit();
}
void DidShowSuggestions() override {
ASSERT_TRUE(loop_runner_ != nullptr);
ASSERT_TRUE(loop_runner_->loop_running());
loop_runner_->Quit();
}
void OnTextFieldChanged() override {
if (!waiting_for_text_change_)
return;
waiting_for_text_change_ = false;
ASSERT_TRUE(loop_runner_ != nullptr);
ASSERT_TRUE(loop_runner_->loop_running());
loop_runner_->Quit();
}
void Reset() {
loop_runner_ = new content::MessageLoopRunner();
}
void Wait() {
loop_runner_->Run();
}
void WaitForTextChange() {
waiting_for_text_change_ = true;
loop_runner_->Run();
}
void SetIsExpectingDynamicRefill(bool expect_refill) {
is_expecting_dynamic_refill_ = expect_refill;
}
private:
scoped_refptr<content::MessageLoopRunner> loop_runner_;
bool waiting_for_text_change_ = false;
bool is_expecting_dynamic_refill_ = false;
DISALLOW_COPY_AND_ASSIGN(AutofillManagerTestDelegateImpl);
};
// Searches all frames of |web_contents| and returns one called |name|. If // Searches all frames of |web_contents| and returns one called |name|. If
// there are none, returns null, if there are more, returns an arbitrary one. // there are none, returns null, if there are more, returns an arbitrary one.
content::RenderFrameHost* RenderFrameHostForName( content::RenderFrameHost* RenderFrameHostForName(
...@@ -236,35 +175,15 @@ content::RenderFrameHost* RenderFrameHostForName( ...@@ -236,35 +175,15 @@ content::RenderFrameHost* RenderFrameHostForName(
} // namespace } // namespace
// AutofillInteractiveTest ----------------------------------------------------
class AutofillInteractiveTest : public InProcessBrowserTest { class AutofillInteractiveTest : public AutofillUiTest {
protected: protected:
AutofillInteractiveTest() : AutofillInteractiveTest() {}
key_press_event_sink_( ~AutofillInteractiveTest() override = default;
base::Bind(&AutofillInteractiveTest::HandleKeyPressEvent,
base::Unretained(this))) {}
~AutofillInteractiveTest() override {}
// InProcessBrowserTest: // InProcessBrowserTest:
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
// Don't want Keychain coming up on Mac. AutofillUiTest::SetUpOnMainThread();
test::DisableSystemServices(browser()->profile()->GetPrefs());
// Inject the test delegate into the AutofillManager.
content::WebContents* web_contents = GetWebContents();
ContentAutofillDriver* autofill_driver =
ContentAutofillDriverFactory::FromWebContents(web_contents)
->DriverForFrame(web_contents->GetMainFrame());
AutofillManager* autofill_manager = autofill_driver->autofill_manager();
autofill_manager->SetTestDelegate(&test_delegate_);
// If the mouse happened to be over where the suggestions are shown, then
// the preview will show up and will fail the tests. We need to give it a
// point that's within the browser frame, or else the method hangs.
gfx::Point reset_mouse(GetWebContents()->GetContainerBounds().origin());
reset_mouse = gfx::Point(reset_mouse.x() + 5, reset_mouse.y() + 5);
ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(reset_mouse));
// Ensure that |embedded_test_server()| serves both domains used below. // Ensure that |embedded_test_server()| serves both domains used below.
host_resolver()->AddRule("*", "127.0.0.1"); host_resolver()->AddRule("*", "127.0.0.1");
...@@ -272,14 +191,7 @@ class AutofillInteractiveTest : public InProcessBrowserTest { ...@@ -272,14 +191,7 @@ class AutofillInteractiveTest : public InProcessBrowserTest {
} }
void TearDownOnMainThread() override { void TearDownOnMainThread() override {
// Make sure to close any showing popups prior to tearing down the UI. AutofillUiTest::TearDownOnMainThread();
content::WebContents* web_contents = GetWebContents();
AutofillManager* autofill_manager =
ContentAutofillDriverFactory::FromWebContents(web_contents)
->DriverForFrame(web_contents->GetMainFrame())
->autofill_manager();
autofill_manager->client()->HideAutofillPopup();
test::ReenableSystemServices();
} }
content::WebContents* GetWebContents() { content::WebContents* GetWebContents() {
...@@ -444,79 +356,6 @@ class AutofillInteractiveTest : public InProcessBrowserTest { ...@@ -444,79 +356,6 @@ class AutofillInteractiveTest : public InProcessBrowserTest {
ExpectFieldValue("phone", "15125551234"); ExpectFieldValue("phone", "15125551234");
} }
void SendKeyToPageAndWait(ui::DomKey key) {
ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
SendKeyToPageAndWait(key, code, key_code);
}
void SendKeyToPageAndWait(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code) {
test_delegate_.Reset();
content::SimulateKeyPress(GetWebContents(), key, code, key_code, false,
false, false, false);
test_delegate_.Wait();
}
bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event) {
return true;
}
void SendKeyToPopupAndWait(ui::DomKey key) {
ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
SendKeyToPopupAndWait(key, code, key_code,
GetRenderViewHost()->GetWidget());
}
void SendKeyToPopupAndWait(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code,
content::RenderWidgetHost* widget) {
// Route popup-targeted key presses via the render view host.
content::NativeWebKeyboardEvent event(blink::WebKeyboardEvent::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
ui::EventTimeForNow());
event.windows_key_code = key_code;
event.dom_code = static_cast<int>(code);
event.dom_key = key;
test_delegate_.Reset();
// Install the key press event sink to ensure that any events that are not
// handled by the installed callbacks do not end up crashing the test.
widget->AddKeyPressEventCallback(key_press_event_sink_);
widget->ForwardKeyboardEvent(event);
test_delegate_.Wait();
widget->RemoveKeyPressEventCallback(key_press_event_sink_);
}
void SendKeyToDataListPopup(ui::DomKey key) {
ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code);
SendKeyToDataListPopup(key, code, key_code);
}
// Datalist does not support autofill preview. There is no need to start
// message loop for Datalist.
void SendKeyToDataListPopup(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code) {
// Route popup-targeted key presses via the render view host.
content::NativeWebKeyboardEvent event(blink::WebKeyboardEvent::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
ui::EventTimeForNow());
event.windows_key_code = key_code;
event.dom_code = static_cast<int>(code);
event.dom_key = key;
// Install the key press event sink to ensure that any events that are not
// handled by the installed callbacks do not end up crashing the test.
GetRenderViewHost()->GetWidget()->AddKeyPressEventCallback(
key_press_event_sink_);
GetRenderViewHost()->GetWidget()->ForwardKeyboardEvent(event);
GetRenderViewHost()->GetWidget()->RemoveKeyPressEventCallback(
key_press_event_sink_);
}
void TryBasicFormFill() { void TryBasicFormFill() {
FocusFirstNameField(); FocusFirstNameField();
...@@ -565,11 +404,7 @@ class AutofillInteractiveTest : public InProcessBrowserTest { ...@@ -565,11 +404,7 @@ class AutofillInteractiveTest : public InProcessBrowserTest {
SendKeyToPopupAndWait(ui::DomKey::ENTER); SendKeyToPopupAndWait(ui::DomKey::ENTER);
} }
AutofillManagerTestDelegateImpl* test_delegate() { return &test_delegate_; }
private: private:
AutofillManagerTestDelegateImpl test_delegate_;
net::TestURLFetcherFactory url_fetcher_factory_; net::TestURLFetcherFactory url_fetcher_factory_;
// KeyPressEventCallback that serves as a sink to ensure that every key press // KeyPressEventCallback that serves as a sink to ensure that every key press
......
This diff is collapsed.
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_H_
#define CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_H_
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_manager_test_delegate.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace autofill {
class AutofillManagerTestDelegateImpl
: public autofill::AutofillManagerTestDelegate {
public:
AutofillManagerTestDelegateImpl();
~AutofillManagerTestDelegateImpl() override;
// autofill::AutofillManagerTestDelegate:
void DidPreviewFormData() override;
void DidFillFormData() override;
void DidShowSuggestions() override;
void OnTextFieldChanged() override;
void Reset();
void Wait();
void WaitForTextChange();
bool WaitForPreviewFormData(base::TimeDelta timeout);
bool WaitForFormDataFilled(base::TimeDelta timeout);
bool WaitForSuggestionShown(base::TimeDelta timeout);
bool WaitForTextChange(base::TimeDelta timeout);
void SetIsExpectingDynamicRefill(bool expect_refill) {
is_expecting_dynamic_refill_ = expect_refill;
}
private:
scoped_refptr<content::MessageLoopRunner> loop_runner_;
bool is_expecting_dynamic_refill_ = false;
bool waiting_for_preview_form_data_;
bool waiting_for_fill_form_data_;
bool waiting_for_show_suggestion_;
bool waiting_for_text_change_;
DISALLOW_COPY_AND_ASSIGN(AutofillManagerTestDelegateImpl);
};
class AutofillUiTest : public InProcessBrowserTest {
protected:
AutofillUiTest();
~AutofillUiTest() override;
// InProcessBrowserTest:
void SetUpOnMainThread() override;
void TearDownOnMainThread() override;
bool TryFillForm(std::string focus_element_css_selector, int attempts = 1);
bool ShowAutofillSuggestion(std::string focus_element_css_selector);
void SendKeyToPageAndWait(ui::DomKey key);
void SendKeyToPageAndWait(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code);
void SendKeyToPopupAndWait(ui::DomKey key);
void SendKeyToPopupAndWait(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code,
content::RenderWidgetHost* widget);
void SendKeyToDataListPopup(ui::DomKey key);
void SendKeyToDataListPopup(ui::DomKey key,
ui::DomCode code,
ui::KeyboardCode key_code);
bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event);
content::WebContents* GetWebContents();
content::RenderViewHost* GetRenderViewHost();
AutofillManagerTestDelegateImpl* test_delegate() { return &test_delegate_; };
content::RenderWidgetHost::KeyPressEventCallback key_press_event_sink();
private:
void SendKeyToPage(ui::DomKey key);
void SendKeyToPopup(ui::DomKey key);
AutofillManagerTestDelegateImpl test_delegate_;
// KeyPressEventCallback that serves as a sink to ensure that every key press
// event the tests create and have the WebContents forward is handled by some
// key press event callback. It is necessary to have this sinkbecause if no
// key press event callback handles the event (at least on Mac), a DCHECK
// ends up going off that the |event| doesn't have an |os_event| associated
// with it.
content::RenderWidgetHost::KeyPressEventCallback key_press_event_sink_;
DISALLOW_COPY_AND_ASSIGN(AutofillUiTest);
};
} // namespace autofill
#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_H_
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
namespace autofill { namespace autofill {
// PdmChangeWaiter ------------------------------------------------------------
// This class is used to wait for asynchronous updates to PersonalDataManager // This class is used to wait for asynchronous updates to PersonalDataManager
// to complete. // to complete.
class PdmChangeWaiter : public PersonalDataManagerObserver { class PdmChangeWaiter : public PersonalDataManagerObserver {
...@@ -70,6 +71,15 @@ void AddTestProfile(Browser* browser, const AutofillProfile& profile) { ...@@ -70,6 +71,15 @@ void AddTestProfile(Browser* browser, const AutofillProfile& profile) {
observer.Wait(); observer.Wait();
} }
void AddTestProfile(Browser* browser,
const AutofillProfile& profile,
const CreditCard& card) {
AddTestProfile(browser, profile);
PdmChangeWaiter observer(browser);
GetPersonalDataManager(browser->profile())->AddCreditCard(card);
observer.Wait();
}
void SetTestProfile(Browser* browser, const AutofillProfile& profile) { void SetTestProfile(Browser* browser, const AutofillProfile& profile) {
std::vector<AutofillProfile> profiles; std::vector<AutofillProfile> profiles;
profiles.push_back(profile); profiles.push_back(profile);
...@@ -91,4 +101,13 @@ void AddTestCreditCard(Browser* browser, const CreditCard& card) { ...@@ -91,4 +101,13 @@ void AddTestCreditCard(Browser* browser, const CreditCard& card) {
observer.Wait(); observer.Wait();
} }
void AddTestAutofillData(Browser* browser,
const AutofillProfile& profile,
const CreditCard& card) {
AddTestProfile(browser, profile);
PdmChangeWaiter observer(browser);
GetPersonalDataManager(browser->profile())->AddCreditCard(card);
observer.Wait();
}
} // namespace autofill } // namespace autofill
...@@ -17,9 +17,10 @@ class CreditCard; ...@@ -17,9 +17,10 @@ class CreditCard;
void AddTestProfile(Browser* browser, const AutofillProfile& profile); void AddTestProfile(Browser* browser, const AutofillProfile& profile);
void SetTestProfile(Browser* browser, const AutofillProfile& profile); void SetTestProfile(Browser* browser, const AutofillProfile& profile);
void SetTestProfiles(Browser* browser, std::vector<AutofillProfile>* profiles); void SetTestProfiles(Browser* browser, std::vector<AutofillProfile>* profiles);
void AddTestCreditCard(Browser* browser, const CreditCard& card); void AddTestCreditCard(Browser* browser, const CreditCard& card);
void AddTestAutofillData(Browser* browser,
const AutofillProfile& profile,
const CreditCard& card);
} // namespace autofill } // namespace autofill
#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_UTIL_H_ #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_UITEST_UTIL_H_
...@@ -4555,6 +4555,8 @@ if (!is_android) { ...@@ -4555,6 +4555,8 @@ if (!is_android) {
"../browser/apps/app_window_interactive_uitest.h", "../browser/apps/app_window_interactive_uitest.h",
"../browser/apps/guest_view/web_view_interactive_browsertest.cc", "../browser/apps/guest_view/web_view_interactive_browsertest.cc",
"../browser/autofill/autofill_interactive_uitest.cc", "../browser/autofill/autofill_interactive_uitest.cc",
"../browser/autofill/autofill_uitest.cc",
"../browser/autofill/autofill_uitest.h",
"../browser/autofill/autofill_uitest_util.cc", "../browser/autofill/autofill_uitest_util.cc",
"../browser/autofill/autofill_uitest_util.h", "../browser/autofill/autofill_uitest_util.h",
"../browser/browser_keyevents_browsertest.cc", "../browser/browser_keyevents_browsertest.cc",
...@@ -5488,6 +5490,79 @@ if (!is_android && !is_fuchsia) { ...@@ -5488,6 +5490,79 @@ if (!is_android && !is_fuchsia) {
assert_no_deps = [ "//chrome" ] assert_no_deps = [ "//chrome" ]
} }
# Tests autofill on captured websites
test("autofill_captured_sites_interactive_tests") {
sources = [
"../browser/autofill/autofill_captured_sites_interactive_uitest.cc",
"../browser/autofill/autofill_uitest.cc",
"../browser/autofill/autofill_uitest.h",
"../browser/autofill/autofill_uitest_util.cc",
"../browser/autofill/autofill_uitest_util.h",
"base/interactive_test_utils.cc",
"base/interactive_test_utils.h",
"base/interactive_test_utils_aura.cc",
"base/interactive_test_utils_aura.h",
"base/interactive_test_utils_common_views.cc",
"base/interactive_test_utils_mac.mm",
"base/interactive_test_utils_win.cc",
"base/interactive_ui_tests_main.cc",
]
configs += [ "//build/config:precompiled_headers" ]
data = [
"//chrome/test/data/autofill/captured_sites/",
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
ldflags = []
deps = [
":test_support",
":test_support_ui",
"//chrome:packed_resources",
"//chrome:resources",
"//chrome:strings",
"//chrome/browser",
"//chrome/browser/devtools",
"//chrome/renderer",
"//components/resources",
"//components/sync",
"//components/sync:test_support_model",
"//content/app/resources",
"//content/test:test_support",
"//crypto:platform",
"//crypto:test_support",
"//google_apis:test_support",
"//net",
"//net:net_resources",
"//net:test_support",
"//skia",
"//testing/gmock",
"//testing/gtest",
"//third_party/hunspell",
"//third_party/icu",
"//third_party/libpng",
"//third_party/zlib",
"//ui/base:test_support",
"//ui/resources:ui_test_pak",
"//ui/web_dialogs:test_support",
]
if (is_win) {
deps += [
"//chrome:other_version",
"//third_party/wtl",
"//ui/resources",
]
configs -= [ "//build/config/win:default_incremental_linking" ]
configs +=
[ "//build/config/win:default_large_module_incremental_linking" ]
}
}
group("telemetry_unittests") { group("telemetry_unittests") {
testonly = true testonly = true
deps = [ deps = [
......
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
const automation_helper = (function() {
var automation_helper = {
// An enum specifying the state of an element on the page.
DomElementReadyState:
Object.freeze({
"present": 0,
"visible": 1,
"enabled": 2,
"on_top": 4,
}),
};
// public:
// Checks if an element is present, visible or enabled on the page.
automation_helper.isElementReady = function(
getElementFunction,
state_flags =
this.DomElementReadyState.visible |
this.DomElementReadyState.enabled |
this.DomElementReadyState.on_top) {
const element = getElementFunction();
if (element) {
var isReady = true;
if (state_flags & this.DomElementReadyState.visible) {
var target = element;
// In some custom select drop downs, like the ones on Amazon.com and
// Zappos.com, the drop down options are hosted inside a span element
// that is the immediate sibling, rather than the descendant, of the
// select dropdown.
// In these cases, check if the span is visible instead.
if (element.offsetParent === null &&
element instanceof HTMLSelectElement &&
element.nextElementSibling instanceof HTMLSpanElement)
target = element.nextElementSibling;
isReady =
(target.offsetParent !== null) &&
(target.offsetWidth > 0) &&
(target.offsetHeight > 0);
if (isReady && state_flags & this.DomElementReadyState.on_top) {
var rect = target.getBoundingClientRect();
isReady &=
// Check that the element is not concealed behind another element.
isSelfOrDescendant(
target,
document.elementFromPoint(
// As coordinates, use the center of the element, minus
// the window offset in case the element is outside the
// view.
rect.left + rect.width / 2 - window.pageXOffset,
rect.top + rect.height / 2 - window.pageYOffset));
}
}
if (isReady && state_flags & this.DomElementReadyState.enabled) {
isReady = !element.disabled;
}
return isReady;
}
return false;
};
// Check if an element identified by a selector is present, visible or
// enabled on the page.
automation_helper.isElementWithSelectorReady = function(
selector,
state_flags = this.DomElementReadyState.visible
| this.DomElementReadyState.enabled) {
return this.isElementReady(
function(){
return document.querySelector(selector);
},
state_flags);
};
// Simulates the user selecting a dropdown option by setting the dropdown
// option and then fire an onchange event on the dropdown element.
automation_helper.selectOptionFromDropDownElementByIndex =
function (dropdown, index) {
dropdown.options.selectedIndex = index;
triggerOnChangeEventOnElement(dropdown);
};
// Simulates the user interacting with an input element by setting the input
// value and then fire
// an onchange event on the input element.
automation_helper.setInputElementValue = function (element, value) {
element.value = value;
triggerOnChangeEventOnElement(element);
};
// private:
// Triggers an onchange event on a dom element.
function triggerOnChangeEventOnElement(element) {
var event = document.createEvent('HTMLEvents');
event.initEvent('change', false, true);
element.dispatchEvent(event);
}
function isSelfOrDescendant(parent, child) {
var node = child;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
return automation_helper;
})();
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDWDCCAkACCQCjJ6dB+xU8zTANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAoMCVJpY2sg
TGFiczEUMBIGA1UECwwLTW9ydHkgR3JvdXAxFjAUBgNVBAMMDVJpY2sgTGFicyBJ
bmMwHhcNMTgwMTI5MTcwNTQyWhcNMjgwMTI3MTcwNTQyWjBuMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAoMCVJpY2sg
TGFiczEUMBIGA1UECwwLTW9ydHkgR3JvdXAxFjAUBgNVBAMMDVJpY2sgTGFicyBJ
bmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvan2SUSEOhQe2pVH2
59NQEet3ekDqpojqPLQ5i7ZC9YQccZQqNpq5uNd6jesABMkoa4tgBCV7VwyiRJfz
1jM6d1Rv/pVSWBUaB1kbAAgwmdXQ+kyfij99xMJVgThFydXQ8d7lX53aPzitqr8M
nQL1FxcWQZQojBXEXfx1evtrz86SXh/USWI1SmXUPi3Dtb2XqkhKzR56/MM1DDNB
GFwxbvr5jDOt4lSF88YnbI8hyuaC7D38sMjeM+peNBDhnW2JRUQzbAJxz7b1pbxr
f6JAi2YYz7MFX3vsIRDqsV79oBlg5F0Q0XMioh710SwzQlb2Evhg85Lr2pAprStr
owe1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBALDIsZwlKykoJbz9oOmlm6GHqiBA
FVD23ulvw+WDS18zDXmtYuKfXxecXKqFuI+Q90TinZBIqqBTRw7ncRm8apBxC2cr
mwQ+7H6pC1yiI5xL3wQvPSs7+HWMVApEo0wD7/HWxC42Wo7Sh3tWJ5Pp0zSiCeOC
7IKaN26mpsH+HCT3SDFIAkxP93ZZ+VLR+0BTeHplop8bj4ApMrcwFSn7PmMP7+mD
Z6TaZfMjzuoX0Uw56WemxnbEhTj0xP2bEK7LlC2vvykzfBvx5r8qTTr4QFN4NaOW
/JUrXf1RNvkF0R+C09ftRJqBRU15vPQF9bZ4jFqUAvMIw/6ABTY+2MgwOkk=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDvan2SUSEOhQe2
pVH259NQEet3ekDqpojqPLQ5i7ZC9YQccZQqNpq5uNd6jesABMkoa4tgBCV7Vwyi
RJfz1jM6d1Rv/pVSWBUaB1kbAAgwmdXQ+kyfij99xMJVgThFydXQ8d7lX53aPzit
qr8MnQL1FxcWQZQojBXEXfx1evtrz86SXh/USWI1SmXUPi3Dtb2XqkhKzR56/MM1
DDNBGFwxbvr5jDOt4lSF88YnbI8hyuaC7D38sMjeM+peNBDhnW2JRUQzbAJxz7b1
pbxrf6JAi2YYz7MFX3vsIRDqsV79oBlg5F0Q0XMioh710SwzQlb2Evhg85Lr2pAp
rStrowe1AgMBAAECggEAVyGPX6+s8ybQ/tS9trj5+5r58fqJI5zGGVFMWeERzN6j
XOKtBIvo1YyCxzRdAR28w7wX9jKsHW45x9hk4LXk4PNKxJOHbndl/poMALqLQHzM
BwkA19Mtdi0keI/Vlfh1m1xbPWkF/sahbvrEGugNinsDyPLG49bPmS6HUY4XcE11
X2orbBHX2jXV2vTWYOd3hpN9Fjf4LC9tkLnSCWV946vqCULX6vNeiFvgYYNsQL7+
68m8IJbLz/Zp4akAg7QBE999HbiYRsDriEbIqi1KYFYSLJARK/HLB2QJIGTcUORN
kqJRGHH8whqiP3HRyPFjTg9MmDTnK4ck2CgPIbhTYQKBgQD+xUNzwpgQ/bQaTXOJ
EpOaVW2xpkNAxeYfLi84MaevH05rQpq7UJTarvQaMQRRAiEhOnnPE6XW1DLi/ufn
DmSdJ4WInLWFQvZCOX6uR6nlL1B0fir5p0ZJ/8JYLmsZJyuEWuQtnbWXx5lrafnj
dxbkuokLfWM+uGb0XuyOvDWB3QKBgQDwkkImps688lxS4Y5BEXSml0mGk/93ddSk
8r6u4jyNVKeA7BiJl/fBLSELWx6wmnBRn40ruiOYdiZ9XNdBHpiqLC8GyLNw1gH+
G8AtWlQQRp+mHCYd7YGm3FVxR3xOr0LvhD88VQiApcFJ9kAMpw4x/+tl7wWIU/ha
ozvAjC97uQKBgQDJTIfdQVXxpuPVHHk7RuhFO1au1RWyaitHW7CHkARUBEJr0XZ8
plDW8QGCaFRmxW3z2UZBbd7M/WEvpgUJtX+eBIonxNpP7vawoMkNXyZIJbhGMyI1
nAuXkHO4RQTuHNrvevlraX7/6/+2tUdyiLgYBaMe5vWxqvX+A7qZlPAaKQKBgDvx
lb4fzl/SXCLixRl9if2AsPdxpTRVDflT6yOUh9YHg/1h1vNiBIAECLnVqJ4EvXv5
L6n3par1tb2DBrfZMd0lSUp9dAjnqnNw4r/qRAwSLPGtDepjJe039apWT6kjjqAz
6bXU0luL8c/GJkuAzM60oCk0F8LSeY3MIrVluOcxAoGAQCRVEQaYl35X4xcP+dZV
oyNC1WiDwUhnsJ15BhB4N+NbasI+xcszuunYZVMTwI+i9wQpY03ABDvr3skxQ1tW
y9//wNhmVcTZghS2MRW03Bktv0QJxl5lm8GBGWvAFOJYo/MZirDI1jJHy0yDtxqe
qP5kp/Ncp4eHubLjVdstcQc=
-----END PRIVATE KEY-----
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment