diff --git a/frontend/test/__support__/enzyme_utils.js b/frontend/test/__support__/enzyme_utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..a748ba9f984fa43c6e7ec9c6082ce8a051b5e2de
--- /dev/null
+++ b/frontend/test/__support__/enzyme_utils.js
@@ -0,0 +1,37 @@
+import Button from "metabase/components/Button";
+
+export const click = (enzymeWrapper) => {
+    const nodeType = enzymeWrapper.type();
+    if (nodeType === Button || nodeType === "button") {
+        console.trace(
+            'You are calling `click` for a button; you would probably want to use `clickButton` instead as ' +
+            'it takes all button click scenarios into account.'
+        )
+    }
+    // Normal click event. Works for both `onClick` React event handlers and react-router <Link> objects.
+    // We simulate a left button click with `{ button: 0 }` because react-router requires that.
+    enzymeWrapper.simulate('click', { button: 0 });
+}
+
+export const clickButton = (enzymeWrapper) => {
+    // `clickButton` is separate from `click` because `wrapper.closest(..)` sometimes results in error
+    // if the parent element isn't found, https://github.com/airbnb/enzyme/issues/410
+
+    // Submit event must be called on the button component itself (not its child components), otherwise it won't work
+    const closestButton = enzymeWrapper.closest("button");
+
+    if (closestButton.length === 1) {
+        closestButton.simulate("submit"); // for forms with onSubmit
+        closestButton.simulate("click", { button: 0 }); // for lone buttons / forms without onSubmit
+    } else {
+        // Assume that the current component wraps a button element
+        enzymeWrapper.simulate("submit");
+        enzymeWrapper.simulate("click", { button: 0 });
+    }
+}
+
+export const setInputValue = (inputWrapper, value, { blur = true } = {}) => {
+    inputWrapper.simulate('change', { target: { value: value } });
+    if (blur) inputWrapper.simulate("blur")
+}
+
diff --git a/frontend/test/__support__/integrated_tests.js b/frontend/test/__support__/integrated_tests.js
index 8ad4814f81e8a0c7affe9a4fab8b12f25003ca10..80d5939ce0506fe0fab6fae503e59e9da2681b26 100644
--- a/frontend/test/__support__/integrated_tests.js
+++ b/frontend/test/__support__/integrated_tests.js
@@ -20,7 +20,7 @@ import { Provider } from 'react-redux';
 
 import { createMemoryHistory } from 'history'
 import { getStore } from "metabase/store";
-import { createRoutes, Link, Router, useRouterHistory } from "react-router";
+import { createRoutes, Router, useRouterHistory } from "react-router";
 import _ from 'underscore';
 import chalk from "chalk";
 
@@ -34,7 +34,6 @@ import { getRoutes as getPublicRoutes } from "metabase/routes-public";
 import { getRoutes as getEmbedRoutes } from "metabase/routes-embed";
 
 import moment from "moment";
-import Button from "metabase/components/Button";
 
 let hasStartedCreatingStore = false;
 let hasFinishedCreatingStore = false
@@ -326,40 +325,6 @@ const testStoreEnhancer = (createStore, history, getRoutes) => {
     }
 }
 
-export const click = (enzymeWrapper) => {
-    const nodeType = enzymeWrapper.type();
-    if (nodeType === Button || nodeType === "button") {
-        console.warn(
-            'You are calling `click` for a button; you would probably want to use `clickButton` instead as ' +
-            'it takes all button click scenarios into account.'
-        )
-    }
-    // Normal click event. Works for both `onClick` React event handlers and react-router <Link> objects.
-    // We simulate a left button click with `{ button: 0 }` because react-router requires that.
-    enzymeWrapper.simulate('click', { button: 0 });
-}
-
-// DEPRECATED
-export const clickRouterLink = click
-
-export const clickButton = (enzymeWrapper) => {
-    const closestButton = enzymeWrapper.closest("button");
-
-    if (closestButton.length === 1) {
-        closestButton.simulate("submit"); // for forms with onSubmit
-        closestButton.simulate("click"); // for lone buttons / forms without onSubmit
-    } else {
-        // Assume that the current component wraps a button element
-        enzymeWrapper.simulate("submit");
-        enzymeWrapper.simulate("click");
-    }
-}
-
-export const setInputValue = (inputWrapper, value, { blur = true } = {}) => {
-    inputWrapper.simulate('change', { target: { value: value } });
-    if (blur) inputWrapper.simulate("blur")
-}
-
 // Commonly used question helpers that are temporarily here
 // TODO Atte Keinänen 6/27/17: Put all metabase-lib -related test helpers to one file
 export const createSavedQuestion = async (unsavedQuestion) => {
diff --git a/frontend/test/admin/databases/DatabaseListApp.integ.spec.js b/frontend/test/admin/databases/DatabaseListApp.integ.spec.js
index e22cc0b4b68a2fe2d685197a7951350c55b18a3a..b0523b0e529c15acd484ef63d399f8f842b52dc5 100644
--- a/frontend/test/admin/databases/DatabaseListApp.integ.spec.js
+++ b/frontend/test/admin/databases/DatabaseListApp.integ.spec.js
@@ -1,10 +1,12 @@
 import {
     login,
-    createTestStore,
+    createTestStore
+} from "__support__/integrated_tests";
+import {
     click,
     clickButton,
     setInputValue
-} from "__support__/integrated_tests";
+} from "__support__/enzyme_utils";
 
 import { mount } from "enzyme";
 import {
@@ -164,7 +166,7 @@ describe('dashboard list', () => {
 
             const deleteModal = wrapper.find('.test-modal')
             setInputValue(deleteModal.find('.Form-input'), "DELETE")
-            click(deleteModal.find('.Button.Button--danger'));
+            clickButton(deleteModal.find('.Button.Button--danger'));
 
             // test that the modal is gone
             expect(wrapper.find('.test-modal').length).toEqual(0)
@@ -207,7 +209,7 @@ describe('dashboard list', () => {
             const deleteModal = wrapper.find('.test-modal')
 
             setInputValue(deleteModal.find('.Form-input'), "DELETE");
-            click(deleteModal.find('.Button.Button--danger'))
+            clickButton(deleteModal.find('.Button.Button--danger'))
 
             // test that the modal is gone
             expect(wrapper.find('.test-modal').length).toEqual(0)
diff --git a/frontend/test/admin/datamodel/FieldApp.integ.spec.js b/frontend/test/admin/datamodel/FieldApp.integ.spec.js
index b422b33a8f28052b72f0944931eac779574da0c3..d25f17d5a3be837d1810a00177117a159be77e44 100644
--- a/frontend/test/admin/datamodel/FieldApp.integ.spec.js
+++ b/frontend/test/admin/datamodel/FieldApp.integ.spec.js
@@ -1,8 +1,14 @@
 import {
     login,
-    createTestStore, clickButton,
+    createTestStore,
 } from "__support__/integrated_tests";
 
+import {
+    clickButton,
+    setInputValue,
+    click
+} from "__support__/enzyme_utils";
+
 import {
     DELETE_FIELD_DIMENSION,
     deleteFieldDimension,
@@ -80,11 +86,11 @@ describe("FieldApp", () => {
             const descriptionInput = header.find(Input).at(1);
             expect(descriptionInput.props().value).toBe(staticFixtureMetadata.fields['1'].description);
 
-            nameInput.simulate('change', {target: {value: newTitle}});
+            setInputValue(nameInput, newTitle);
             await store.waitForActions([UPDATE_FIELD])
             store.resetDispatchedActions();
 
-            descriptionInput.simulate('change', {target: {value: newDescription}});
+            setInputValue(descriptionInput, newDescription);
             await store.waitForActions([UPDATE_FIELD])
         })
 
@@ -125,7 +131,7 @@ describe("FieldApp", () => {
 
             const visibilitySelect = fieldApp.find(FieldVisibilityPicker);
             visibilitySelect.simulate('click');
-            visibilitySelect.find(TestPopover).find("li").at(1).children().first().simulate("click");
+            click(visibilitySelect.find(TestPopover).find("li").at(1).children().first());
 
             await store.waitForActions([UPDATE_FIELD])
         })
@@ -163,7 +169,7 @@ describe("FieldApp", () => {
             typeSelect.simulate('click');
 
             const noSpecialTypeButton = typeSelect.find(TestPopover).find("li").last().children().first()
-            noSpecialTypeButton.simulate("click");
+            click(noSpecialTypeButton);
 
             await store.waitForActions([UPDATE_FIELD])
             expect(picker.text()).toMatch(/Select a special type/);
@@ -180,7 +186,7 @@ describe("FieldApp", () => {
                 .filterWhere(li => li.text() === "Number").first()
                 .children().first();
 
-            noSpecialTypeButton.simulate("click");
+            click(noSpecialTypeButton);
 
             await store.waitForActions([UPDATE_FIELD])
             expect(picker.text()).toMatch(/Number/);
@@ -193,7 +199,7 @@ describe("FieldApp", () => {
             typeSelect.simulate('click');
 
             const foreignKeyButton = typeSelect.find(TestPopover).find("li").at(2).children().first();
-            foreignKeyButton.simulate("click");
+            click(foreignKeyButton);
             await store.waitForActions([UPDATE_FIELD])
             store.resetDispatchedActions();
 
@@ -338,12 +344,12 @@ describe("FieldApp", () => {
             const firstMapping = fieldValueMappings.at(0);
             expect(firstMapping.find("h3").text()).toBe("1");
             expect(firstMapping.find(Input).props().value).toBe("1");
-            firstMapping.find(Input).simulate('change', {target: {value: "Terrible"}});
+            setInputValue(firstMapping.find(Input), "Terrible")
 
             const lastMapping = fieldValueMappings.last();
             expect(lastMapping.find("h3").text()).toBe("5");
             expect(lastMapping.find(Input).props().value).toBe("5");
-            lastMapping.find(Input).simulate('change', {target: {value: "Extraordinarily awesome"}});
+            setInputValue(lastMapping.find(Input), "Extraordinarily awesome")
 
             const saveButton = valueRemappingsSection.find(ButtonWithStatus)
             clickButton(saveButton)
diff --git a/frontend/test/admin/datamodel/datamodel.integ.spec.js b/frontend/test/admin/datamodel/datamodel.integ.spec.js
index 781fc3f05acdb312395952e7ac6ac3284648a934..e65a43726289bdd3b6bf1a293d6b8b7717036612 100644
--- a/frontend/test/admin/datamodel/datamodel.integ.spec.js
+++ b/frontend/test/admin/datamodel/datamodel.integ.spec.js
@@ -1,8 +1,13 @@
 // Converted from an old Selenium E2E test
 import {
     login,
-    createTestStore, clickRouterLink,
+    createTestStore
 } from "__support__/integrated_tests";
+import {
+    click,
+    clickButton,
+    setInputValue
+} from "__support__/enzyme_utils"
 import { mount } from "enzyme";
 import {
     CREATE_METRIC,
@@ -44,7 +49,7 @@ describe("admin/datamodel", () => {
 
             // Open "Orders" table section
             const adminListItems = app.find(".AdminList-item");
-            adminListItems.at(0).simulate("click");
+            click(adminListItems.at(0));
             await store.waitForActions([SELECT_TABLE]);
             store.resetDispatchedActions()
 
@@ -62,7 +67,7 @@ describe("admin/datamodel", () => {
             app.find("#VisibilityTypes > span").at(0).simulate("click");
 
             // Open "People" table section
-            adminListItems.at(1).simulate("click");
+            click(adminListItems.at(1));
             await store.waitForActions([SELECT_TABLE]);
             store.resetDispatchedActions()
 
@@ -70,28 +75,28 @@ describe("admin/datamodel", () => {
             // Set Address field to "Only in Detail Views"
             const columnsListItems = app.find(ColumnsList).find("li")
 
-            columnsListItems.first().find(".TableEditor-field-visibility").simulate("click");
+            click(columnsListItems.first().find(".TableEditor-field-visibility"));
             const onlyInDetailViewsRow = app.find(ColumnarSelector).find(".ColumnarSelector-row").at(1)
             expect(onlyInDetailViewsRow.text()).toMatch(/Only in Detail Views/);
-            onlyInDetailViewsRow.simulate("click");
+            click(onlyInDetailViewsRow);
             await store.waitForActions([UPDATE_FIELD]);
             store.resetDispatchedActions();
 
             // Set Birth Date field to "Do Not Include"
-            columnsListItems.at(1).find(".TableEditor-field-visibility").simulate("click");
+            click(columnsListItems.at(1).find(".TableEditor-field-visibility"));
             // different ColumnarSelector than before so do a new lookup
             const doNotIncludeRow = app.find(ColumnarSelector).find(".ColumnarSelector-row").at(2)
             expect(doNotIncludeRow.text()).toMatch(/Do Not Include/);
-            doNotIncludeRow.simulate("click");
+            click(doNotIncludeRow);
 
             await store.waitForActions([UPDATE_FIELD]);
             store.resetDispatchedActions();
 
             // modify special type for address field
-            columnsListItems.first().find(".TableEditor-field-special-type").simulate("click")
+            click(columnsListItems.first().find(".TableEditor-field-special-type"))
             const entityNameTypeRow = app.find(ColumnarSelector).find(".ColumnarSelector-row").at(1)
             expect(entityNameTypeRow.text()).toMatch(/Entity Name/);
-            entityNameTypeRow.simulate("click");
+            click(entityNameTypeRow);
             await store.waitForActions([UPDATE_FIELD]);
 
             // TODO Atte Keinänen 8/9/17: Currently this test doesn't validate that the updates actually are reflected in QB
@@ -108,30 +113,30 @@ describe("admin/datamodel", () => {
             store.resetDispatchedActions();
 
             // Click the new segment button and check that we get properly redirected
-            clickRouterLink(app.find(SegmentsList).find(Link));
+            click(app.find(SegmentsList).find(Link));
             expect(store.getPath()).toBe('/admin/datamodel/segment/create?table=2')
             await store.waitForActions([FETCH_TABLE_METADATA, UPDATE_PREVIEW_SUMMARY]);
             store.resetDispatchedActions();
 
             // Add "Email Is Not gmail" filter
-            app.find(".GuiBuilder-filtered-by a").first().simulate("click");
+            click(app.find(".GuiBuilder-filtered-by a").first())
 
             const filterPopover = app.find(FilterPopover);
-            filterPopover.find(FieldList).find('h4[children="Email"]').simulate("click");
+            click(filterPopover.find(FieldList).find('h4[children="Email"]'));
 
             const operatorSelector = filterPopover.find(OperatorSelector);
-            operatorSelector.find('button[children="Is not"]').simulate("click");
+            clickButton(operatorSelector.find('button[children="Is not"]'));
 
             const addFilterButton = filterPopover.find(".Button.disabled");
 
             filterPopover.find('textarea.border-purple').simulate('change', { target: { value: "gmail" }})
-            await addFilterButton.simulate("click");
+            await clickButton(addFilterButton);
 
             await store.waitForActions([UPDATE_PREVIEW_SUMMARY]);
 
             // Add name and description
-            app.find("input[name='name']").simulate('change', { target: { value: 'Gmail users' }});
-            app.find("textarea[name='description']").simulate("change", { target: { value: 'All people using Gmail for email'}});
+            setInputValue(app.find("input[name='name']"), "Gmail users")
+            setInputValue(app.find("textarea[name='description']"), "change")
 
             // Save the segment
             app.find('button[children="Save changes"]').simulate("click");
@@ -155,11 +160,11 @@ describe("admin/datamodel", () => {
             store.resetDispatchedActions();
 
             // Click the new metric button and check that we get properly redirected
-            clickRouterLink(app.find(MetricsList).find(Link));
+            click(app.find(MetricsList).find(Link));
             expect(store.getPath()).toBe('/admin/datamodel/metric/create?table=2')
             await store.waitForActions([FETCH_TABLE_METADATA, UPDATE_PREVIEW_SUMMARY]);
 
-            app.find("#Query-section-aggregation").simulate("click");
+            click(app.find("#Query-section-aggregation"));
             app.find("#AggregationPopover").find('h4[children="Count of rows"]').simulate("click");
 
             app.find("input[name='name']").simulate('change', { target: { value: 'User count' }});
diff --git a/frontend/test/admin/settings/SettingsAuthenticationOptions.integ.spec.js b/frontend/test/admin/settings/SettingsAuthenticationOptions.integ.spec.js
index 144e64dca1940a9a4088090ecf8869d420a3d29a..4e6410577552ca39ae5fa1a32de7e1ccfdf089d3 100644
--- a/frontend/test/admin/settings/SettingsAuthenticationOptions.integ.spec.js
+++ b/frontend/test/admin/settings/SettingsAuthenticationOptions.integ.spec.js
@@ -1,8 +1,8 @@
 import {
     login,
-    createTestStore,
-    clickRouterLink,
+    createTestStore
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils"
 
 import { mount } from "enzyme";
 
@@ -28,13 +28,13 @@ describe('Admin Auth Options', () => {
         const settingsWrapper = app.find(SettingsEditorApp)
         const authListItem = settingsWrapper.find('span[children="Authentication"]')
 
-        clickRouterLink(authListItem)
+        click(authListItem)
 
         expect(settingsWrapper.find(SettingsAuthenticationOptions).length).toBe(1)
 
         // test google
         const googleConfigButton = settingsWrapper.find('.Button').first()
-        clickRouterLink(googleConfigButton)
+        click(googleConfigButton)
 
         expect(settingsWrapper.find(SettingsSingleSignOnForm).length).toBe(1)
 
@@ -42,7 +42,7 @@ describe('Admin Auth Options', () => {
 
         // test ldap
         const ldapConfigButton = settingsWrapper.find('.Button').last()
-        clickRouterLink(ldapConfigButton)
+        click(ldapConfigButton)
         expect(settingsWrapper.find(SettingsLdapForm).length).toBe(1)
     })
 })
diff --git a/frontend/test/dashboard/dashboard.integ.spec.js b/frontend/test/dashboard/dashboard.integ.spec.js
index 701f7c488a80ae7f74e0f155d0574bf08b3c564b..cad4d19095e83bc478eef3b2e133dab8b9dd8721 100644
--- a/frontend/test/dashboard/dashboard.integ.spec.js
+++ b/frontend/test/dashboard/dashboard.integ.spec.js
@@ -2,6 +2,10 @@ import {
     createTestStore,
     login
 } from "__support__/integrated_tests";
+import {
+    click, clickButton,
+    setInputValue
+} from "__support__/enzyme_utils"
 
 import { DashboardApi, PublicApi } from "metabase/services";
 import * as Urls from "metabase/lib/urls";
@@ -109,14 +113,14 @@ describe("Dashboard", () => {
             await store.waitForActions([FETCH_DASHBOARD])
 
             // Test dashboard renaming
-            app.find(".Icon.Icon-pencil").simulate("click");
+            click(app.find(".Icon.Icon-pencil"));
             await store.waitForActions([SET_EDITING_DASHBOARD]);
 
             const headerInputs = app.find(".Header-title input")
-            headerInputs.first().simulate("change", {target: {value: "Customer Analysis Paralysis"}})
-            headerInputs.at(1).simulate("change", {target: {value: ""}}) // Test empty description
+            setInputValue(headerInputs.first(), "Customer Analysis Paralysis")
+            setInputValue(headerInputs.at(1), "")
 
-            app.find(EditBar).find(".Button--primary.Button").simulate("click");
+            clickButton(app.find(EditBar).find(".Button--primary.Button"));
             await store.waitForActions([SAVE_DASHBOARD_AND_CARDS, FETCH_DASHBOARD])
 
             await delay(200)
@@ -133,9 +137,9 @@ describe("Dashboard", () => {
             await store.waitForActions([FETCH_DASHBOARD])
 
             // Test parameter filter creation
-            app.find(".Icon.Icon-pencil").simulate("click");
+            click(app.find(".Icon.Icon-pencil"));
             await store.waitForActions([SET_EDITING_DASHBOARD]);
-            app.find(".Icon.Icon-funneladd").simulate("click");
+            click(app.find(".Icon.Icon-funneladd"));
             // Choose Time filter type
             app.find(ParameterOptionsSection)
                 .filterWhere((section) => section.text().match(/Time/))
@@ -148,11 +152,11 @@ describe("Dashboard", () => {
 
             await store.waitForActions(ADD_PARAMETER)
 
-            app.find(ParameterValueWidget).simulate("click");
-            app.find(PredefinedRelativeDatePicker).find("button[children='Yesterday']").simulate("click");
+            click(app.find(ParameterValueWidget));
+            clickButton(app.find(PredefinedRelativeDatePicker).find("button[children='Yesterday']"));
             expect(app.find(ParameterValueWidget).text()).toEqual("Yesterday");
 
-            app.find(HeaderModal).find("button[children='Done']").simulate("click")
+            clickButton(app.find(HeaderModal).find("button[children='Done']"))
 
             // Wait until the header modal exit animation is finished
             await store.waitForActions([SET_EDITING_PARAMETER_ID])
diff --git a/frontend/test/home/HomepageApp.integ.spec.js b/frontend/test/home/HomepageApp.integ.spec.js
index f269136747b21797bc8dd2787046e177f10ac09f..74a65737c1dbc867aa3020c9ae3d0520ab67f191 100644
--- a/frontend/test/home/HomepageApp.integ.spec.js
+++ b/frontend/test/home/HomepageApp.integ.spec.js
@@ -1,7 +1,9 @@
 import {
     login,
-    createTestStore, createSavedQuestion, clickRouterLink
+    createTestStore,
+    createSavedQuestion
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils"
 
 import React from 'react';
 import { mount } from "enzyme";
@@ -76,7 +78,7 @@ describe("HomepageApp", () => {
 
             const activityFeed = homepageApp.find(Activity);
             const metricLink = activityFeed.find(ActivityItem).find('a[children="Vendor count"]').first();
-            clickRouterLink(metricLink)
+            click(metricLink)
             
             await store.waitForActions([QUERY_COMPLETED]);
             expect(app.find(Scalar).text()).toBe("200");
diff --git a/frontend/test/modes/actions/SummarizeBySegmentMetricAction.unit.spec.js b/frontend/test/modes/actions/SummarizeBySegmentMetricAction.unit.spec.js
index 2298be260b0b652af9890de853f691ca4ae99d7a..cdc7121830c700770bcd474828fdd0cd39b46c25 100644
--- a/frontend/test/modes/actions/SummarizeBySegmentMetricAction.unit.spec.js
+++ b/frontend/test/modes/actions/SummarizeBySegmentMetricAction.unit.spec.js
@@ -55,9 +55,7 @@ describe("SummarizeBySegmentMetricAction", () => {
                 });
 
                 const component = mount(popover);
-                component
-                    .find('.List-item-title[children="Count of rows"]')
-                    .simulate("click");
+                click(component.find('.List-item-title[children="Count of rows"]'));
             });
         });
 
@@ -74,8 +72,7 @@ describe("SummarizeBySegmentMetricAction", () => {
                 });
 
                 const component = mount(popover);
-                component
-                    .find('.List-item-title[children="Sum of ..."]')
+                component.find('.List-item-title[children="Sum of ..."]')
                     .simulate("click");
 
                 component
diff --git a/frontend/test/parameters/parameters.integ.spec.js b/frontend/test/parameters/parameters.integ.spec.js
index 3ab79b387f8c1ac7c4b14e35f2f53749894444cf..1d3a154fe4722ec731cf43c33de28788aac3cae6 100644
--- a/frontend/test/parameters/parameters.integ.spec.js
+++ b/frontend/test/parameters/parameters.integ.spec.js
@@ -5,6 +5,11 @@ import {
     createTestStore,
     restorePreviousLogin
 } from "__support__/integrated_tests";
+import {
+    click, clickButton,
+    setInputValue
+} from "__support__/enzyme_utils"
+
 import { mount } from "enzyme";
 
 import { LOAD_CURRENT_USER } from "metabase/redux/user";
@@ -81,7 +86,7 @@ describe("parameters", () => {
             expect(enabledToggleContainer.text()).toBe("Disabled");
 
             // toggle it on
-            enabledToggleContainer.find(Toggle).simulate("click");
+            click(enabledToggleContainer.find(Toggle));
             await store.waitForActions([UPDATE_SETTING])
 
             // make sure it's enabled
@@ -119,7 +124,7 @@ describe("parameters", () => {
             const app = mount(store.getAppContainer())
             await store.waitForActions([INITIALIZE_QB]);
 
-            app.find(".Icon-sql").simulate("click");
+            click(app.find(".Icon-sql"));
             await store.waitForActions([SET_QUERY_MODE]);
             store.resetDispatchedActions();
 
@@ -129,42 +134,43 @@ describe("parameters", () => {
 
             const fieldFilterVarType = tagEditorSidebar.find('.ColumnarSelector-row').at(3);
             expect(fieldFilterVarType.text()).toBe("Field Filter");
-            fieldFilterVarType.simulate("click");
+            click(fieldFilterVarType);
 
             await store.waitForActions([UPDATE_TEMPLATE_TAG]);
             store.resetDispatchedActions();
 
             await delay(100);
 
-            tagEditorSidebar.find(".TestPopoverBody .AdminSelect").first().simulate("change", {target: {value: "cat"}})
+            setInputValue(tagEditorSidebar.find(".TestPopoverBody .AdminSelect").first(), "cat")
             const categoryRow = tagEditorSidebar.find(".TestPopoverBody .ColumnarSelector-row").first();
             expect(categoryRow.text()).toBe("ProductsCategory");
-            categoryRow.simulate("click");
+            click(categoryRow);
 
             await store.waitForActions([UPDATE_TEMPLATE_TAG, FETCH_FIELD_VALUES])
 
             // close the template variable sidebar
-            tagEditorSidebar.find(".Icon-close").simulate("click");
+            click(tagEditorSidebar.find(".Icon-close"));
 
             // test without the parameter
-            app.find(RunButton).simulate("click");
+            click(app.find(RunButton));
             await store.waitForActions([RUN_QUERY, QUERY_COMPLETED])
             await store.resetDispatchedActions();
             expect(app.find(Scalar).text()).toBe(COUNT_ALL);
 
             // test the parameter
-            app.find(Parameters).find("a").first().simulate("click");
-            app.find(CategoryWidget).find('li[children="Doohickey"]').simulate("click");
-            app.find(RunButton).simulate("click");
+            click(app.find(Parameters).find("a").first());
+            click(app.find(CategoryWidget).find('li[children="Doohickey"]'));
+            click(app.find(RunButton));
             await store.waitForActions([RUN_QUERY, QUERY_COMPLETED])
             expect(app.find(Scalar).text()).toBe(COUNT_DOOHICKEY);
 
             // save the question, required for public link/embedding
             app.find(".Header-buttonSection a").first().find("a").simulate("click");
             await store.waitForActions([LOAD_COLLECTIONS]);
-            app.find(SaveQuestionModal).find("input[name='name']").first().simulate("change", {target: {value: "sql parametrized"}})
 
-            app.find(SaveQuestionModal).find("button").last().simulate("click");
+            setInputValue(app.find(SaveQuestionModal).find("input[name='name']"), "sql parametrized");
+
+            clickButton(app.find(SaveQuestionModal).find("button").last());
             await store.waitForActions([NOTIFY_CARD_CREATED]);
 
             app.find('#QuestionSavedModal .Button[children="Not now"]').simulate("click");
@@ -172,29 +178,29 @@ describe("parameters", () => {
             await delay(200);
 
             // open sharing panel
-            app.find(".Icon-share").simulate("click");
+            click(app.find(".Icon-share"));
 
             // "Embed this question in an application"
-            app.find(SharingPane).find("h3").last().simulate("click");
+            click(app.find(SharingPane).find("h3").last());
 
             // make the parameter editable
-            app.find(".AdminSelect-content[children='Disabled']").simulate("click");
+            click(app.find(".AdminSelect-content[children='Disabled']"));
 
             app.find(".TestPopoverBody .Icon-pencil").simulate("click");
 
             await delay(200);
 
-            app.find("div[children='Publish']").simulate("click");
+            click(app.find("div[children='Publish']"));
             await store.waitForActions([UPDATE_ENABLE_EMBEDDING, UPDATE_EMBEDDING_PARAMS])
 
             // save the embed url for next tests
             embedUrl = getRelativeUrlWithoutHash(app.find(PreviewPane).find("iframe").prop("src"));
 
             // back to main share panel
-            app.find(EmbedTitle).simulate("click");
+            click(app.find(EmbedTitle));
 
             // toggle public link on
-            app.find(SharingPane).find(Toggle).simulate("click");
+            click(app.find(SharingPane).find(Toggle));
             await store.waitForActions([CREATE_PUBLIC_LINK]);
 
             // save the public url for next tests
@@ -217,8 +223,8 @@ describe("parameters", () => {
                 expect(app.find(Scalar).text()).toBe(COUNT_ALL + "sql parametrized");
 
                 // manually click parameter (sadly the query results loading happens inline again)
-                app.find(Parameters).find("a").first().simulate("click");
-                app.find(CategoryWidget).find('li[children="Doohickey"]').simulate("click");
+                click(app.find(Parameters).find("a").first());
+                click(app.find(CategoryWidget).find('li[children="Doohickey"]'));
                 await delay(200);
                 expect(app.find(Scalar).text()).toBe(COUNT_DOOHICKEY + "sql parametrized");
 
diff --git a/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js b/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js
index 7d03420bf17fd381e654edb6dcf3f4b086b5e00c..5b7c344fd7f6874a22b8c79c9cecf1c32db8a0c0 100644
--- a/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js
+++ b/frontend/test/query_builder/components/dataref/FieldPane.integ.spec.js
@@ -2,6 +2,7 @@ import {
     login,
     createTestStore
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils";
 
 import React from 'react';
 import { mount } from "enzyme";
@@ -38,13 +39,13 @@ describe("FieldPane", () => {
 
     it("opens properly from QB", async () => {
         // open data reference sidebar by clicking button
-        queryBuilder.find(".Icon-reference").simulate("click");
+        click(queryBuilder.find(".Icon-reference"));
         await store.waitForActions([TOGGLE_DATA_REFERENCE]);
 
         const dataReference = queryBuilder.find(DataReference);
         expect(dataReference.length).toBe(1);
 
-        dataReference.find('a[children="Orders"]').simulate("click");
+        click(dataReference.find('a[children="Orders"]'));
 
         // TODO: Refactor TablePane so that it uses redux/metadata actions instead of doing inlined API calls
         // then we can replace this with `store.waitForActions([FETCH_TABLE_FOREIGN_KEYS])` or similar
@@ -76,7 +77,7 @@ describe("FieldPane", () => {
         const distinctValuesButton = queryBuilder.find(DataReference).find(QueryButton).at(0);
 
         try {
-            distinctValuesButton.children().first().simulate("click");
+            click(distinctValuesButton.children().first());
         } catch(e) {
             // QueryButton uses react-router Link which always throws an error if it's called without a parent Router object
             // Now we are just using the onClick handler of Link so we don't have to care about that
diff --git a/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js b/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js
index f85868c36ee9914a52dcd896a92adad076e3fee1..8697aeb67be15fcd161e84bbe609859b7329caec 100644
--- a/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js
+++ b/frontend/test/query_builder/components/dataref/MetricPane.integ.spec.js
@@ -2,6 +2,7 @@ import {
     login,
     createTestStore
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils"
 
 import React from 'react';
 import { mount } from "enzyme";
@@ -37,20 +38,20 @@ describe("MetricPane", () => {
 
     it("opens properly from QB", async () => {
         // open data reference sidebar by clicking button
-        queryBuilder.find(".Icon-reference").simulate("click");
+        click(queryBuilder.find(".Icon-reference"));
         await store.waitForActions([TOGGLE_DATA_REFERENCE]);
 
         const dataReference = queryBuilder.find(DataReference);
         expect(dataReference.length).toBe(1);
 
-        dataReference.find('a[children="Products"]').simulate("click");
+        click(dataReference.find('a[children="Products"]'));
 
         // TODO: Refactor TablePane so that it uses redux/metadata actions instead of doing inlined API calls
         // then we can replace this with `store.waitForActions([FETCH_TABLE_FOREIGN_KEYS])` or similar
         await delay(3000)
 
         store.resetDispatchedActions() // make sure that we wait for the newest actions
-        dataReference.find(`a[children="${vendor_count_metric.name}"]`).first().simulate("click")
+        click(dataReference.find(`a[children="${vendor_count_metric.name}"]`).first())
 
         await store.waitForActions([FETCH_TABLE_METADATA]);
     });
@@ -64,7 +65,7 @@ describe("MetricPane", () => {
         const queryButton = queryBuilder.find(DataReference).find(QueryButton);
 
         try {
-            queryButton.children().first().simulate("click");
+            click(queryButton.children().first());
         } catch(e) {
             // QueryButton uses react-router Link which always throws an error if it's called without a parent Router object
             // Now we are just using the onClick handler of Link so we don't have to care about that
diff --git a/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js b/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js
index e45afcc036de3e94e76a9dc11ab6106a7046e338..20fdd89fb458b0ffa766abff38f8a246274054c8 100644
--- a/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js
+++ b/frontend/test/query_builder/components/dataref/SegmentPane.integ.spec.js
@@ -2,6 +2,7 @@ import {
     login,
     createTestStore
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils";
 
 import React from 'react';
 import { mount } from "enzyme";
@@ -51,20 +52,20 @@ describe("SegmentPane", () => {
 
     it("opens properly from QB", async () => {
         // open data reference sidebar by clicking button
-        queryBuilder.find(".Icon-reference").simulate("click");
+        click(queryBuilder.find(".Icon-reference"));
         await store.waitForActions([TOGGLE_DATA_REFERENCE]);
 
         const dataReference = queryBuilder.find(DataReference);
         expect(dataReference.length).toBe(1);
 
-        dataReference.find('a[children="Orders"]').simulate("click");
+        click(dataReference.find('a[children="Orders"]'));
 
         // TODO: Refactor TablePane so that it uses redux/metadata actions instead of doing inlined API calls
         // then we can replace this with `store.waitForActions([FETCH_TABLE_FOREIGN_KEYS])` or similar
         await delay(3000)
 
         store.resetDispatchedActions() // make sure that we wait for the newest actions
-        dataReference.find(`a[children="${orders_past_30_days_segment.name}"]`).first().simulate("click")
+        click(dataReference.find(`a[children="${orders_past_30_days_segment.name}"]`).first())
 
         await store.waitForActions([FETCH_TABLE_METADATA]);
     });
@@ -80,7 +81,7 @@ describe("SegmentPane", () => {
         await store.waitForActions(LOAD_TABLE_METADATA);
 
         const filterByButton = queryBuilder.find(DataReference).find(UseForButton).first();
-        filterByButton.children().first().simulate("click");
+        click(filterByButton.children().first());
 
         await store.waitForActions([QUERY_COMPLETED]);
         store.resetDispatchedActions()
@@ -92,7 +93,7 @@ describe("SegmentPane", () => {
         const numberQueryButton = queryBuilder.find(DataReference).find(QueryButton).at(0);
 
         try {
-            numberQueryButton.children().first().simulate("click");
+            click(numberQueryButton.children().first());
         } catch(e) {
             // QueryButton uses react-router Link which always throws an error if it's called without a parent Router object
             // Now we are just using the onClick handler of Link so we don't have to care about that
@@ -110,7 +111,7 @@ describe("SegmentPane", () => {
         const allQueryButton = queryBuilder.find(DataReference).find(QueryButton).at(1);
 
         try {
-            allQueryButton.children().first().simulate("click");
+            click(allQueryButton.children().first());
         } catch(e) {
             // QueryButton uses react-router Link which always throws an error if it's called without a parent Router object
             // Now we are just using the onClick handler of Link so we don't have to care about that
diff --git a/frontend/test/query_builder/query_builder.integ.spec.js b/frontend/test/query_builder/query_builder.integ.spec.js
index 097458d32c7a7884013aa4e2225cea35d7ada9c8..a14cbfca8f69b874f53f5717640474b4c0c5a458 100644
--- a/frontend/test/query_builder/query_builder.integ.spec.js
+++ b/frontend/test/query_builder/query_builder.integ.spec.js
@@ -2,8 +2,12 @@ import {
     login,
     whenOffline,
     createSavedQuestion,
-    createTestStore,
+    createTestStore
 } from "__support__/integrated_tests";
+import {
+    click,
+    clickButton, setInputValue
+} from "__support__/enzyme_utils"
 
 import React from 'react';
 import QueryBuilder from "metabase/query_builder/containers/QueryBuilder";
@@ -106,11 +110,11 @@ describe("QueryBuilder", () => {
             const { store, qb } = await initQBWithReviewsTable();
 
             // Run the raw data query
-            qb.find(RunButton).simulate("click");
+            click(qb.find(RunButton));
             await store.waitForActions([QUERY_COMPLETED]);
 
             const vizSettings = qb.find(VisualizationSettings);
-            vizSettings.find(".Icon-gear").simulate("click");
+            click(vizSettings.find(".Icon-gear"));
 
             const settingsModal = vizSettings.find(".test-modal")
             const table = settingsModal.find(TableSimple);
@@ -123,12 +127,12 @@ describe("QueryBuilder", () => {
             const fieldsToIncludeCheckboxes = settingsModal.find(CheckBox)
             expect(fieldsToIncludeCheckboxes.length).toBe(6)
 
-            fieldsToIncludeCheckboxes.filterWhere((checkbox) => checkbox.parent().find("span").text() === "Created At").simulate("click");
+            click(fieldsToIncludeCheckboxes.filterWhere((checkbox) => checkbox.parent().find("span").text() === "Created At"))
 
             expect(table.find('div[children="Created At"]').length).toBe(0);
 
             // Save the settings
-            doneButton.simulate("click");
+            click(doneButton);
             expect(vizSettings.find(".test-modal").length).toBe(0);
 
             // Don't test the contents of actual table visualization here as react-virtualized doesn't seem to work
@@ -169,7 +173,7 @@ describe("QueryBuilder", () => {
 
             const runButton = qbWrapper.find(RunButton);
             expect(runButton.text()).toBe("Cancel");
-            expect(runButton.simulate("click"));
+            click(runButton);
 
             await store.waitForActions([CANCEL_QUERY, QUERY_ERRORED]);
             expect(qbWrapper.find(QueryHeader).find("h1").text()).toBe(savedQuestion.displayName())
@@ -226,7 +230,7 @@ describe("QueryBuilder", () => {
 
                 const runButton = qbWrapper.find(RunButton);
                 expect(runButton.text()).toBe("Cancel");
-                expect(runButton.simulate("click"));
+                click(runButton);
 
                 await store.waitForActions([CANCEL_QUERY, QUERY_ERRORED]);
                 expect(qbWrapper.find(QueryHeader).find("h1").text()).toBe("New question")
@@ -269,13 +273,13 @@ describe("QueryBuilder", () => {
                 // would make testing with selectors more natural
                 const filterSection = qb.find('.GuiBuilder-filtered-by');
                 const addFilterButton = filterSection.find('.AddButton');
-                addFilterButton.simulate("click");
+                click(addFilterButton);
 
                 const filterPopover = filterSection.find(FilterPopover);
 
                 const ratingFieldButton = filterPopover.find(FieldList).find('h4[children="Rating"]')
                 expect(ratingFieldButton.length).toBe(1);
-                ratingFieldButton.simulate('click');
+                click(ratingFieldButton);
             })
 
             it("lets you see its field values in filter popover", () => {
@@ -296,11 +300,11 @@ describe("QueryBuilder", () => {
                 const widgetCheckbox = widgetFieldItem.find(CheckBox);
 
                 expect(widgetCheckbox.props().checked).toBe(false);
-                widgetFieldItem.children().first().simulate("click");
+                click(widgetFieldItem.children().first());
                 expect(widgetCheckbox.props().checked).toBe(true);
 
                 const addFilterButton = filterPopover.find('button[children="Add filter"]')
-                addFilterButton.simulate("click");
+                clickButton(addFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 store.resetDispatchedActions();
@@ -314,7 +318,7 @@ describe("QueryBuilder", () => {
             it("lets you set 'Rating is 5 or 4' filter", async () => {
                 // reopen the filter popover by clicking filter widget
                 const filterWidget = qb.find(FilterWidget);
-                filterWidget.find(FieldName).simulate('click');
+                click(filterWidget.find(FieldName));
 
                 const filterPopover = qb.find(FilterPopover);
                 const fieldItems = filterPopover.find('li');
@@ -322,11 +326,11 @@ describe("QueryBuilder", () => {
                 const gadgetCheckbox = widgetFieldItem.find(CheckBox);
 
                 expect(gadgetCheckbox.props().checked).toBe(false);
-                widgetFieldItem.children().first().simulate("click");
+                click(widgetFieldItem.children().first());
                 expect(gadgetCheckbox.props().checked).toBe(true);
 
                 const addFilterButton = filterPopover.find('button[children="Update filter"]')
-                addFilterButton.simulate("click");
+                clickButton(addFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
 
@@ -336,7 +340,7 @@ describe("QueryBuilder", () => {
 
             it("lets you remove the added filter", async () => {
                 const filterWidget = qb.find(FilterWidget);
-                filterWidget.find(".Icon-close").simulate('click');
+                click(filterWidget.find(".Icon-close"))
                 await store.waitForActions([SET_DATASET_QUERY])
 
                 expect(qb.find(FilterWidget).length).toBe(0);
@@ -353,13 +357,13 @@ describe("QueryBuilder", () => {
             it("lets you add ID field as a filter", async () => {
                 const filterSection = qb.find('.GuiBuilder-filtered-by');
                 const addFilterButton = filterSection.find('.AddButton');
-                addFilterButton.simulate("click");
+                click(addFilterButton);
 
                 const filterPopover = filterSection.find(FilterPopover);
 
                 const ratingFieldButton = filterPopover.find(FieldList).find('h4[children="ID"]')
                 expect(ratingFieldButton.length).toBe(1);
-                ratingFieldButton.simulate('click');
+                click(ratingFieldButton)
             })
 
             it("lets you see a correct number of operators in filter popover", () => {
@@ -367,7 +371,7 @@ describe("QueryBuilder", () => {
 
                 const operatorSelector = filterPopover.find(OperatorSelector);
                 const moreOptionsIcon = operatorSelector.find(".Icon-chevrondown");
-                moreOptionsIcon.simulate("click");
+                click(moreOptionsIcon);
 
                 expect(operatorSelector.find("button").length).toBe(9)
             })
@@ -375,10 +379,10 @@ describe("QueryBuilder", () => {
             it("lets you set 'ID is 10' filter", async () => {
                 const filterPopover = qb.find(FilterPopover);
                 const filterInput = filterPopover.find("textarea");
-                filterInput.simulate('change', { target: { value: "10" }})
+                setInputValue(filterInput, "10")
 
                 const addFilterButton = filterPopover.find('button[children="Add filter"]')
-                addFilterButton.simulate("click");
+                clickButton(addFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 store.resetDispatchedActions();
@@ -391,16 +395,16 @@ describe("QueryBuilder", () => {
 
             it("lets you update the filter to 'ID is 10 or 11'", async () => {
                 const filterWidget = qb.find(FilterWidget);
-                filterWidget.find(FieldName).simulate('click');
+                click(filterWidget.find(FieldName))
 
                 const filterPopover = qb.find(FilterPopover);
                 const filterInput = filterPopover.find("textarea");
 
                 // Intentionally use a value with lots of extra spaces
-                filterInput.simulate('change', { target: { value: "  10,      11" }})
+                setInputValue(filterInput, "  10,      11")
 
                 const addFilterButton = filterPopover.find('button[children="Update filter"]')
-                addFilterButton.simulate("click");
+                clickButton(addFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
 
@@ -410,25 +414,25 @@ describe("QueryBuilder", () => {
 
             it("lets you update the filter to 'ID is between 1 or 100'", async () => {
                 const filterWidget = qb.find(FilterWidget);
-                filterWidget.find(FieldName).simulate('click');
+                click(filterWidget.find(FieldName))
 
                 const filterPopover = qb.find(FilterPopover);
                 const operatorSelector = filterPopover.find(OperatorSelector);
-                operatorSelector.find('button[children="Between"]').simulate("click");
+                clickButton(operatorSelector.find('button[children="Between"]'));
 
                 const betweenInputs = filterPopover.find("textarea");
                 expect(betweenInputs.length).toBe(2);
 
                 expect(betweenInputs.at(0).props().value).toBe("10, 11");
 
-                betweenInputs.at(1).simulate('change', { target: { value: "asdasd" }})
+                setInputValue(betweenInputs.at(1), "asdasd")
                 const updateFilterButton = filterPopover.find('button[children="Update filter"]')
                 expect(updateFilterButton.props().className).toMatch(/disabled/);
 
-                betweenInputs.at(0).simulate('change', { target: { value: "1" }})
-                betweenInputs.at(1).simulate('change', { target: { value: "100" }})
+                setInputValue(betweenInputs.at(0), "1")
+                setInputValue(betweenInputs.at(1), "100")
 
-                updateFilterButton.simulate("click");
+                clickButton(updateFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 expect(qb.find(FilterPopover).length).toBe(0);
@@ -446,12 +450,12 @@ describe("QueryBuilder", () => {
             it("lets you group by Total with the default binning option", async () => {
                 const breakoutSection = qb.find('.GuiBuilder-groupedBy');
                 const addBreakoutButton = breakoutSection.find('.AddButton');
-                addBreakoutButton.simulate("click");
+                click(addBreakoutButton);
 
                 const breakoutPopover = breakoutSection.find("#BreakoutPopover")
                 const subtotalFieldButton = breakoutPopover.find(FieldList).find('h4[children="Total"]')
                 expect(subtotalFieldButton.length).toBe(1);
-                subtotalFieldButton.simulate('click');
+                click(subtotalFieldButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
 
@@ -460,7 +464,7 @@ describe("QueryBuilder", () => {
             });
             it("produces correct results for default binning option", async () => {
                 // Run the raw data query
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 // We can use the visible row count as we have a low number of result rows
@@ -475,21 +479,21 @@ describe("QueryBuilder", () => {
             })
             it("lets you change the binning strategy to 100 bins", async () => {
                 const breakoutWidget = qb.find(BreakoutWidget).first();
-                breakoutWidget.find(FieldName).children().first().simulate("click")
+                click(breakoutWidget.find(FieldName).children().first())
                 const breakoutPopover = qb.find("#BreakoutPopover")
 
                 const subtotalFieldButton = breakoutPopover.find(FieldList).find('.List-item--selected h4[children="Auto binned"]')
                 expect(subtotalFieldButton.length).toBe(1);
-                subtotalFieldButton.simulate('click');
+                click(subtotalFieldButton)
 
-                qb.find(DimensionPicker).find('a[children="100 bins"]').simulate("click");
+                click(qb.find(DimensionPicker).find('a[children="100 bins"]'));
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 expect(breakoutWidget.text()).toBe("Total: 100 bins");
             });
             it("produces correct results for 100 bins", async () => {
                 store.resetDispatchedActions();
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 expect(qb.find(".ShownRowCount").text()).toBe("Showing 95 rows");
@@ -501,18 +505,18 @@ describe("QueryBuilder", () => {
             })
             it("lets you disable the binning", async () => {
                 const breakoutWidget = qb.find(BreakoutWidget).first();
-                breakoutWidget.find(FieldName).children().first().simulate("click")
+                click(breakoutWidget.find(FieldName).children().first())
                 const breakoutPopover = qb.find("#BreakoutPopover")
 
                 const subtotalFieldButton = breakoutPopover.find(FieldList).find('.List-item--selected h4[children="100 bins"]')
                 expect(subtotalFieldButton.length).toBe(1);
-                subtotalFieldButton.simulate('click');
+                click(subtotalFieldButton);
 
-                qb.find(DimensionPicker).find('a[children="Don\'t bin"]').simulate("click");
+                click(qb.find(DimensionPicker).find('a[children="Don\'t bin"]'));
             });
             it("produces the expected count of rows when no binning", async () => {
                 store.resetDispatchedActions();
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 // We just want to see that there are a lot more rows than there would be if a binning was active
@@ -533,17 +537,17 @@ describe("QueryBuilder", () => {
             it("lets you group by Latitude with the default binning option", async () => {
                 const breakoutSection = qb.find('.GuiBuilder-groupedBy');
                 const addBreakoutButton = breakoutSection.find('.AddButton');
-                addBreakoutButton.simulate("click");
+                click(addBreakoutButton);
 
                 const breakoutPopover = breakoutSection.find("#BreakoutPopover")
 
                 const userSectionButton = breakoutPopover.find(FieldList).find('h3[children="User"]')
                 expect(userSectionButton.length).toBe(1);
-                userSectionButton.simulate("click");
+                click(userSectionButton);
 
                 const subtotalFieldButton = breakoutPopover.find(FieldList).find('h4[children="Latitude"]')
                 expect(subtotalFieldButton.length).toBe(1);
-                subtotalFieldButton.simulate('click');
+                click(subtotalFieldButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
 
@@ -553,7 +557,7 @@ describe("QueryBuilder", () => {
 
             it("produces correct results for default binning option", async () => {
                 // Run the raw data query
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 expect(qb.find(".ShownRowCount").text()).toBe("Showing 18 rows");
@@ -567,14 +571,14 @@ describe("QueryBuilder", () => {
 
             it("lets you group by Latitude with the 'Bin every 1 degree'", async () => {
                 const breakoutWidget = qb.find(BreakoutWidget).first();
-                breakoutWidget.find(FieldName).children().first().simulate("click")
+                click(breakoutWidget.find(FieldName).children().first())
                 const breakoutPopover = qb.find("#BreakoutPopover")
 
                 const subtotalFieldButton = breakoutPopover.find(FieldList).find('.List-item--selected h4[children="Auto binned"]')
                 expect(subtotalFieldButton.length).toBe(1);
-                subtotalFieldButton.simulate('click');
+                click(subtotalFieldButton);
 
-                qb.find(DimensionPicker).find('a[children="Bin every 1 degree"]').simulate("click");
+                click(qb.find(DimensionPicker).find('a[children="Bin every 1 degree"]'));
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 expect(breakoutWidget.text()).toBe("Latitude: 1°");
@@ -582,7 +586,7 @@ describe("QueryBuilder", () => {
             it("produces correct results for 'Bin every 1 degree'", async () => {
                 // Run the raw data query
                 store.resetDispatchedActions();
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 expect(qb.find(".ShownRowCount").text()).toBe("Showing 180 rows");
@@ -611,7 +615,7 @@ describe("QueryBuilder", () => {
                 }));
 
 
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 const table = qb.find(TestTable);
@@ -622,13 +626,13 @@ describe("QueryBuilder", () => {
 
                 const countCell = firstRowCells.last();
                 expect(countCell.text()).toBe("387");
-                countCell.children().first().simulate("click");
+                click(countCell.children().first());
 
                 // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms
                 await delay(150);
 
                 store.resetDispatchedActions();
-                qb.find(ChartClickActions).find('div[children="Zoom in"]').simulate("click");
+                click(qb.find(ChartClickActions).find('div[children="Zoom in"]'));
 
                 store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]);
 
@@ -653,7 +657,7 @@ describe("QueryBuilder", () => {
                     }
                 }));
 
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 const table = qb.find(TestTable);
@@ -664,13 +668,13 @@ describe("QueryBuilder", () => {
 
                 const countCell = firstRowCells.last();
                 expect(countCell.text()).toBe("417");
-                countCell.children().first().simulate("click");
+                click(countCell.children().first());
 
                 // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms
                 await delay(150);
 
                 store.resetDispatchedActions();
-                qb.find(ChartClickActions).find('div[children="Zoom in"]').simulate("click");
+                click(qb.find(ChartClickActions).find('div[children="Zoom in"]'));
 
                 store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]);
 
@@ -698,7 +702,7 @@ describe("QueryBuilder", () => {
                     }
                 }));
 
-                qb.find(RunButton).simulate("click");
+                click(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 const table = qb.find(TestTable);
@@ -710,13 +714,13 @@ describe("QueryBuilder", () => {
 
                 const countCell = firstRowCells.last();
                 expect(countCell.text()).toBe("1,079");
-                countCell.children().first().simulate("click");
+                click(countCell.children().first());
 
                 // Drill-through is delayed in handleVisualizationClick of Visualization.jsx by 100ms
                 await delay(150);
 
                 store.resetDispatchedActions();
-                qb.find(ChartClickActions).find('div[children="Zoom in"]').simulate("click");
+                click(qb.find(ChartClickActions).find('div[children="Zoom in"]'));
 
                 store.waitForActions([NAVIGATE_TO_NEW_CARD, UPDATE_URL, QUERY_COMPLETED]);
 
@@ -767,13 +771,13 @@ describe("QueryBuilder", () => {
                 // open filter popover
                 const filterSection = qb.find('.GuiBuilder-filtered-by');
                 const newFilterButton = filterSection.find('.AddButton');
-                newFilterButton.simulate("click");
+                click(newFilterButton);
 
                 // choose the field to be filtered
                 const filterPopover = filterSection.find(FilterPopover);
                 const ratingFieldButton = filterPopover.find(FieldList).find('h4[children="Rating Description"]')
                 expect(ratingFieldButton.length).toBe(1);
-                ratingFieldButton.simulate('click');
+                click(ratingFieldButton)
 
                 // check that field values seem correct
                 const fieldItems = filterPopover.find('li');
@@ -785,12 +789,12 @@ describe("QueryBuilder", () => {
                 const widgetFieldItem = fieldItems.last();
                 const widgetCheckbox = widgetFieldItem.find(CheckBox);
                 expect(widgetCheckbox.props().checked).toBe(false);
-                widgetFieldItem.children().first().simulate("click");
+                click(widgetFieldItem.children().first());
                 expect(widgetCheckbox.props().checked).toBe(true);
 
                 // add the filter
                 const addFilterButton = filterPopover.find('button[children="Add filter"]')
-                addFilterButton.simulate("click");
+                clickButton(addFilterButton);
 
                 await store.waitForActions([SET_DATASET_QUERY])
                 store.resetDispatchedActions();
@@ -805,7 +809,7 @@ describe("QueryBuilder", () => {
             it("shows remapped value correctly in Raw Data query with Table visualization", async () => {
                 const { store, qb } = await initQBWithReviewsTable();
 
-                qb.find(RunButton).simulate("click");
+                clickButton(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 const table = qb.find(TestTable);
@@ -825,7 +829,7 @@ describe("QueryBuilder", () => {
             it("shows remapped values correctly in Raw Data query with Table visualization", async () => {
                 const { store, qb } = await initQBWithReviewsTable();
 
-                qb.find(RunButton).simulate("click");
+                clickButton(qb.find(RunButton));
                 await store.waitForActions([QUERY_COMPLETED]);
 
                 const table = qb.find(TestTable);
diff --git a/frontend/test/reference/databases.integ.spec.js b/frontend/test/reference/databases.integ.spec.js
index 3862934eb2563335662c80b9c271fde80c6d000d..09bf4f93b939a45465776b3be4ee1a66af738415 100644
--- a/frontend/test/reference/databases.integ.spec.js
+++ b/frontend/test/reference/databases.integ.spec.js
@@ -1,8 +1,8 @@
 import {
     login,
-    createTestStore,
-    clickRouterLink
+    createTestStore
 } from "__support__/integrated_tests";
+import { click } from "__support__/enzyme_utils"
 
 import React from 'react';
 import { mount } from 'enzyme';
@@ -171,7 +171,7 @@ describe("The Reference Section", () => {
 
             const usefulQuestionLink = fieldDetails.find(UsefulQuestions).find(QueryButton).first().find("a");
             expect(usefulQuestionLink.text()).toBe("Number of Orders grouped by Created At")
-            clickRouterLink(usefulQuestionLink);
+            click(usefulQuestionLink);
 
             await store.waitForActions([INITIALIZE_QB, QUERY_COMPLETED]);