From ba8b9bb7d2e56315d309f7ccca532b1c51ed9b2d Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Tue, 4 Oct 2022 13:56:57 +0300
Subject: [PATCH] Move Google Auth toggle to the auth card (#25633)

---
 .../src/metabase-enterprise/auth/index.js     |   6 +-
 .../components/SettingsGoogleForm.jsx         |  92 ------------
 .../SettingsGoogleForm.styled.tsx             |  21 +++
 .../SettingsGoogleForm/SettingsGoogleForm.tsx | 133 ++++++++++++++++++
 .../components/SettingsGoogleForm/index.ts    |   1 +
 .../src/metabase/admin/settings/settings.js   |  26 +++-
 frontend/src/metabase/admin/settings/utils.js |   2 +-
 .../containers/FormikForm/FormikForm.tsx      |  18 ++-
 .../FormikForm/FormikFormViewAdapter.tsx      |   3 +-
 frontend/src/metabase/lib/settings.ts         |  11 +-
 .../metabase/plugins/builtin/auth/google.js   |  20 ++-
 frontend/src/metabase/services.js             |   4 +
 .../admin/settings/settings.cy.spec.js        |  20 ---
 .../admin/settings/sso/google.cy.spec.js      |  84 +++++++----
 14 files changed, 272 insertions(+), 169 deletions(-)
 delete mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
 create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx
 create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx
 create mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts

diff --git a/enterprise/frontend/src/metabase-enterprise/auth/index.js b/enterprise/frontend/src/metabase-enterprise/auth/index.js
index 8bd1e02f255..8c59f20a0ed 100644
--- a/enterprise/frontend/src/metabase-enterprise/auth/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/auth/index.js
@@ -51,7 +51,7 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections =>
       description: t`When enabled, users can additionally log in with email and password.`,
       type: "boolean",
       getHidden: settings =>
-        !settings["google-auth-client-id"] &&
+        !settings["google-auth-enabled"] &&
         !settings["ldap-enabled"] &&
         !settings["saml-enabled"] &&
         !settings["jwt-enabled"],
@@ -62,7 +62,7 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections =>
       description: t`When enabled, administrators will receive an email the first time a user uses Single Sign-On.`,
       type: "boolean",
       getHidden: settings =>
-        !settings["google-auth-client-id"] &&
+        !settings["google-auth-enabled"] &&
         !settings["ldap-enabled"] &&
         !settings["saml-enabled"] &&
         !settings["jwt-enabled"],
@@ -304,6 +304,8 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({
     settings: [
       {
         key: "google-auth-client-id",
+        required: true,
+        autoFocus: true,
       },
       {
         // Default to OSS fields if enterprise SSO is not enabled
diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
deleted file mode 100644
index 19e8cf19981..00000000000
--- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm.jsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React, { Component } from "react";
-import PropTypes from "prop-types";
-import { connect } from "react-redux";
-import { t, jt } from "ttag";
-import _ from "underscore";
-
-import Form, {
-  FormField,
-  FormSubmit,
-  FormMessage,
-} from "metabase/containers/FormikForm";
-
-import { updateSettings } from "metabase/admin/settings/settings";
-import { settingToFormField } from "metabase/admin/settings/utils";
-import Breadcrumbs from "metabase/components/Breadcrumbs";
-import ExternalLink from "metabase/core/components/ExternalLink";
-import MetabaseSettings from "metabase/lib/settings";
-
-const settingsGoogleFormPropTypes = {
-  elements: PropTypes.array,
-  settingValues: PropTypes.object,
-  updateSettings: PropTypes.func,
-};
-
-class SettingsGoogleForm extends Component {
-  render() {
-    const { elements, settingValues, updateSettings } = this.props;
-
-    const setting = name =>
-      _.findWhere(elements, { key: name }) || { key: name };
-    const settingField = name => settingToFormField(setting(name));
-
-    const initialValues = { ...settingValues };
-
-    return (
-      <Form
-        className="mx2"
-        style={{ maxWidth: 520 }}
-        initialValues={initialValues}
-        onSubmit={updateSettings}
-        overwriteOnInitialValuesChange
-      >
-        <Breadcrumbs
-          crumbs={[
-            [t`Authentication`, "/admin/settings/authentication"],
-            [t`Google Sign-In`],
-          ]}
-          className="mb2"
-        />
-        <h2>{t`Sign in with Google`}</h2>
-        <p className="text-medium">
-          {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`}
-        </p>
-        <p className="text-medium">
-          {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${(
-            <ExternalLink
-              href={MetabaseSettings.docsUrl(
-                "people-and-groups/google-and-ldap",
-                "enabling-google-sign-in",
-              )}
-              target="_blank"
-            >
-              {t`here`}
-            </ExternalLink>
-          )}.`}
-        </p>
-        <FormField
-          {...settingField("google-auth-client-id")}
-          title={t`Client ID`}
-          description=""
-          placeholder={t`{your-client-id}.apps.googleusercontent.com`}
-          required
-          autoFocus
-        />
-        <FormField
-          {...settingField("google-auth-auto-create-accounts-domain")}
-          title={t`Domain`}
-        />
-        <div>
-          <FormMessage />
-        </div>
-        <div>
-          <FormSubmit>{t`Save changes`}</FormSubmit>
-        </div>
-      </Form>
-    );
-  }
-}
-
-export default connect(null, { updateSettings })(SettingsGoogleForm);
-
-SettingsGoogleForm.propTypes = settingsGoogleFormPropTypes;
diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx
new file mode 100644
index 00000000000..35ad46b6fed
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx
@@ -0,0 +1,21 @@
+import styled from "@emotion/styled";
+import Form from "metabase/containers/FormikForm";
+import { color } from "metabase/lib/colors";
+
+export const FormRoot = styled(Form)`
+  margin: 0 1rem;
+  max-width: 32.5rem;
+`;
+
+export const FormHeader = styled.h2`
+  margin-top: 1rem;
+`;
+
+export const FormCaption = styled.p`
+  color: ${color("text-medium")};
+`;
+
+export const FormSection = styled.div`
+  display: flex;
+  gap: 0.5rem;
+`;
diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx
new file mode 100644
index 00000000000..6f4dbbb4602
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx
@@ -0,0 +1,133 @@
+import React, { useRef } from "react";
+import { connect } from "react-redux";
+import { jt, t } from "ttag";
+import MetabaseSettings from "metabase/lib/settings";
+import ActionButton from "metabase/components/ActionButton";
+import ExternalLink from "metabase/core/components/ExternalLink";
+import Breadcrumbs from "metabase/components/Breadcrumbs";
+import {
+  FormField,
+  FormMessage,
+  FormSubmit,
+} from "metabase/containers/FormikForm";
+import { updateGoogleSettings } from "metabase/admin/settings/settings";
+import { settingToFormField } from "metabase/admin/settings/utils";
+import {
+  FormCaption,
+  FormSection,
+  FormHeader,
+  FormRoot,
+} from "./SettingsGoogleForm.styled";
+
+export interface SettingElement {
+  key: string;
+}
+
+export interface SettingsGoogleFormProps {
+  elements?: SettingElement[];
+  settingValues?: Record<string, unknown>;
+  onSubmit: (settingValues: Record<string, unknown>) => void;
+}
+
+const SettingsGoogleForm = ({
+  elements = [],
+  settingValues = {},
+  onSubmit,
+}: SettingsGoogleFormProps) => {
+  const isEnabled = Boolean(settingValues["google-auth-enabled"]);
+  const isEnabledRef = useRef(isEnabled);
+
+  const handleSubmit = (values: Record<string, unknown>) => {
+    return onSubmit({ ...values, "google-auth-enabled": isEnabledRef.current });
+  };
+
+  const handleSaveAndEnableClick = (handleSubmit: () => void) => {
+    isEnabledRef.current = true;
+    return handleSubmit();
+  };
+
+  const handleSaveAndNotEnableClick = (handleSubmit: () => void) => {
+    isEnabledRef.current = false;
+    return handleSubmit();
+  };
+
+  return (
+    <FormRoot
+      initialValues={settingValues}
+      renderSubmit={({ canSubmit, handleSubmit }) => (
+        <>
+          <ActionButton
+            actionFn={() => handleSaveAndEnableClick(handleSubmit)}
+            primary={canSubmit}
+            disabled={!canSubmit}
+            normalText={isEnabled ? t`Save changes` : t`Save and enable`}
+            successText={t`Changes saved!`}
+          />
+          {!isEnabled && (
+            <ActionButton
+              actionFn={() => handleSaveAndNotEnableClick(handleSubmit)}
+              disabled={!canSubmit}
+              normalText={t`Save but don't enable`}
+              successText={t`Changes saved!`}
+            />
+          )}
+        </>
+      )}
+      disablePristineSubmit
+      overwriteOnInitialValuesChange
+      onSubmit={handleSubmit}
+    >
+      <Breadcrumbs
+        crumbs={[
+          [t`Authentication`, "/admin/settings/authentication"],
+          [t`Google Sign-In`],
+        ]}
+      />
+      <FormHeader>{t`Sign in with Google`}</FormHeader>
+      <FormCaption>
+        {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`}
+      </FormCaption>
+      <FormCaption>
+        {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${(
+          <ExternalLink key="link" href={getDocsLink()}>{t`here`}</ExternalLink>
+        )}.`}
+      </FormCaption>
+      <FormField
+        {...getField("google-auth-client-id", elements)}
+        title={t`Client ID`}
+        description=""
+        placeholder={t`{your-client-id}.apps.googleusercontent.com`}
+        required
+        autoFocus
+      />
+      <FormField
+        {...getField("google-auth-auto-create-accounts-domain", elements)}
+        title={t`Domain`}
+      />
+      <FormSection>
+        <FormMessage />
+      </FormSection>
+      <FormSection>
+        <FormSubmit>{t`Save changes`}</FormSubmit>
+      </FormSection>
+    </FormRoot>
+  );
+};
+
+const getField = (name: string, elements: SettingElement[]) => {
+  const setting = elements.find(e => e.key === name) ?? { key: name };
+  return settingToFormField(setting);
+};
+
+const getDocsLink = () => {
+  return MetabaseSettings.docsUrl(
+    "people-and-groups/google-and-ldap",
+    "enabling-google-sign-in",
+  );
+};
+
+const mapDispatchToProps = {
+  onSubmit: updateGoogleSettings,
+};
+
+export default connect(null, mapDispatchToProps)(SettingsGoogleForm);
diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts
new file mode 100644
index 00000000000..f3c8fda7595
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts
@@ -0,0 +1 @@
+export { default } from "./SettingsGoogleForm";
diff --git a/frontend/src/metabase/admin/settings/settings.js b/frontend/src/metabase/admin/settings/settings.js
index e802330f339..e97ba898e0c 100644
--- a/frontend/src/metabase/admin/settings/settings.js
+++ b/frontend/src/metabase/admin/settings/settings.js
@@ -4,7 +4,13 @@ import {
   handleActions,
   combineReducers,
 } from "metabase/lib/redux";
-import { SettingsApi, EmailApi, SlackApi, LdapApi } from "metabase/services";
+import {
+  SettingsApi,
+  EmailApi,
+  SlackApi,
+  LdapApi,
+  GoogleApi,
+} from "metabase/services";
 import { refreshSiteSettings } from "metabase/redux/settings";
 
 // ACITON TYPES AND ACTION CREATORS
@@ -150,6 +156,24 @@ export const updateLdapSettings = createThunkAction(
   },
 );
 
+export const UPDATE_GOOGLE_SETTINGS =
+  "metabase/admin/settings/UPDATE_GOOGLE_SETTINGS";
+export const updateGoogleSettings = createThunkAction(
+  UPDATE_GOOGLE_SETTINGS,
+  function (settings) {
+    return async function (dispatch, getState) {
+      try {
+        const result = await GoogleApi.updateSettings(settings);
+        await dispatch(reloadSettings());
+        return result;
+      } catch (error) {
+        console.log("error updating Google settings", settings, error);
+        throw error;
+      }
+    };
+  },
+);
+
 // REDUCERS
 
 export const warnings = handleActions(
diff --git a/frontend/src/metabase/admin/settings/utils.js b/frontend/src/metabase/admin/settings/utils.js
index 27fd4a7d39a..cc30202dc3f 100644
--- a/frontend/src/metabase/admin/settings/utils.js
+++ b/frontend/src/metabase/admin/settings/utils.js
@@ -13,7 +13,7 @@ export const settingToFormField = setting => ({
   placeholder: setting.is_env_setting
     ? t`Using ${setting.env_name}`
     : setting.placeholder || setting.default,
-  validate: setting.required ? value => !value && "required" : null,
+  validate: setting.required ? value => !value && "required" : undefined,
 });
 
 export const settingToFormFieldId = setting => `setting-${setting.key}`;
diff --git a/frontend/src/metabase/containers/FormikForm/FormikForm.tsx b/frontend/src/metabase/containers/FormikForm/FormikForm.tsx
index 99856f92478..6a405cd10d7 100644
--- a/frontend/src/metabase/containers/FormikForm/FormikForm.tsx
+++ b/frontend/src/metabase/containers/FormikForm/FormikForm.tsx
@@ -1,10 +1,10 @@
-import React, { useCallback, useMemo, useState } from "react";
+import React, { ReactNode, useCallback, useMemo, useState } from "react";
 import { t } from "ttag";
 import _ from "underscore";
 import { assocIn, getIn, merge } from "icepick";
 
 // eslint-disable-next-line import/named
-import { Formik, FormikProps, FormikErrors, FormikHelpers } from "formik";
+import { Formik, FormikErrors, FormikHelpers } from "formik";
 
 import {
   BaseFieldValues,
@@ -14,16 +14,13 @@ import {
   PopulatedFormObject,
 } from "metabase-types/forms";
 
-import {
-  makeFormObject,
-  cleanObject,
-  isNestedFieldName,
-  getMaybeNestedValue,
-} from "../formUtils";
+import { OptionalFormViewProps } from "metabase/components/form/FormikCustomForm/types";
+import { makeFormObject, cleanObject, isNestedFieldName } from "../formUtils";
 import FormikFormViewAdapter from "./FormikFormViewAdapter";
 import useInlineFields from "./useInlineFields";
 
-interface FormContainerProps<Values extends BaseFieldValues> {
+interface FormContainerProps<Values extends BaseFieldValues>
+  extends OptionalFormViewProps {
   form?: FormObject<Values>;
 
   fields?: FormFieldDefinition[];
@@ -47,7 +44,8 @@ interface FormContainerProps<Values extends BaseFieldValues> {
   submitTitle?: string;
   onClose?: () => void;
   footerExtraButtons?: any;
-  children?: (opts: any) => any;
+  disablePristineSubmit?: boolean;
+  children?: ReactNode | ((opts: any) => any);
 }
 
 type ServerErrorResponse = {
diff --git a/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx b/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx
index 1b7af57a409..9d4e5b0ef99 100644
--- a/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx
+++ b/frontend/src/metabase/containers/FormikForm/FormikFormViewAdapter.tsx
@@ -60,6 +60,7 @@ function FormikFormViewAdapter<Values extends BaseFieldValues>({
   resetForm,
   initialValues,
   submitForm,
+  disablePristineSubmit = formObject.disablePristineSubmit,
   ...rest
 }: FormikFormViewAdapterProps<Values>) {
   const [active, setActive] = useState<string | null>(null);
@@ -123,7 +124,7 @@ function FormikFormViewAdapter<Values extends BaseFieldValues>({
       invalid={!isValid}
       valid={isValid}
       pristine={!dirty}
-      disablePristineSubmit={formObject.disablePristineSubmit}
+      disablePristineSubmit={disablePristineSubmit}
       values={values}
       submitting={isSubmitting}
       asyncValidate={validateForm}
diff --git a/frontend/src/metabase/lib/settings.ts b/frontend/src/metabase/lib/settings.ts
index 07a84b04459..fab48840d56 100644
--- a/frontend/src/metabase/lib/settings.ts
+++ b/frontend/src/metabase/lib/settings.ts
@@ -71,6 +71,7 @@ export type SettingName =
   | "engines"
   | "ga-code"
   | "ga-enabled"
+  | "google-auth-enabled"
   | "google-auth-client-id"
   | "has-sample-database?"
   | "has-user-setup"
@@ -183,8 +184,8 @@ class Settings {
     return this.get("hide-embed-branding?");
   }
 
-  isGoogleAuthConfigured() {
-    return this.get("google-auth-client-id") != null;
+  isGoogleAuthEnabled() {
+    return this.get("google-auth-enabled");
   }
 
   isLdapEnabled() {
@@ -201,11 +202,7 @@ class Settings {
   }
 
   isSsoEnabled() {
-    return (
-      this.isGoogleAuthConfigured() ||
-      this.isLdapEnabled() ||
-      this.isGoogleAuthConfigured()
-    );
+    return this.isLdapEnabled() || this.isGoogleAuthEnabled();
   }
 
   isPasswordLoginEnabled() {
diff --git a/frontend/src/metabase/plugins/builtin/auth/google.js b/frontend/src/metabase/plugins/builtin/auth/google.js
index 2fdb34e636c..8d31ca830f7 100644
--- a/frontend/src/metabase/plugins/builtin/auth/google.js
+++ b/frontend/src/metabase/plugins/builtin/auth/google.js
@@ -9,7 +9,7 @@ import {
 
 import MetabaseSettings from "metabase/lib/settings";
 
-import AuthenticationOption from "metabase/admin/settings/components/widgets/AuthenticationOption";
+import AuthenticationWidget from "metabase/admin/settings/components/widgets/AuthenticationWidget";
 import SettingsGoogleForm from "metabase/admin/settings/components/SettingsGoogleForm";
 
 PLUGIN_AUTH_PROVIDERS.push(providers => {
@@ -19,7 +19,7 @@ PLUGIN_AUTH_PROVIDERS.push(providers => {
     Button: require("metabase/auth/containers/GoogleButton").default,
   };
 
-  return MetabaseSettings.isGoogleAuthConfigured()
+  return MetabaseSettings.isGoogleAuthEnabled()
     ? [googleProvider, ...providers]
     : providers;
 });
@@ -28,11 +28,15 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections =>
   updateIn(sections, ["authentication", "settings"], settings => [
     ...settings,
     {
-      authName: t`Sign in with Google`,
-      authDescription: t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`,
-      authType: "google",
-      authEnabled: settings => !!settings["google-auth-client-id"],
-      widget: AuthenticationOption,
+      key: "google-auth-enabled",
+      description: null,
+      widget: AuthenticationWidget,
+      getProps: (setting, settings) => ({
+        authType: "google",
+        authName: t`Sign in with Google`,
+        authDescription: t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`,
+        authConfigured: Boolean(settings["google-auth-client-id"]),
+      }),
     },
   ]),
 );
@@ -44,6 +48,8 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({
     settings: [
       {
         key: "google-auth-client-id",
+        required: true,
+        autoFocus: true,
       },
       {
         key: "google-auth-auto-create-accounts-domain",
diff --git a/frontend/src/metabase/services.js b/frontend/src/metabase/services.js
index d031b479173..8691cf6a981 100644
--- a/frontend/src/metabase/services.js
+++ b/frontend/src/metabase/services.js
@@ -240,6 +240,10 @@ export const LdapApi = {
   updateSettings: PUT("/api/ldap/settings"),
 };
 
+export const GoogleApi = {
+  updateSettings: PUT("/api/google/settings"),
+};
+
 export const TimelineApi = {
   list: GET("/api/timeline"),
   listForCollection: GET("/api/collection/:collectionId/timelines"),
diff --git a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
index a1966a94a21..3f86cbc7c3f 100644
--- a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
@@ -61,19 +61,6 @@ describe("scenarios > admin > settings", () => {
     cy.get(".SaveStatus").contains(/^Error: Invalid site URL/);
   });
 
-  it("should render the proper auth options", () => {
-    // Ported from `SettingsAuthenticationOptions.e2e.spec.js`
-    // Google sign in
-    cy.visit("/admin/settings/authentication");
-
-    configureAuth("Sign in with Google");
-
-    cy.contains(
-      "To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID.",
-    );
-    cy.findByText("Save changes");
-  });
-
   it("should save a setting", () => {
     cy.server();
     cy.route("PUT", "**/admin-email").as("saveSettings");
@@ -322,10 +309,3 @@ describeEE("scenarios > admin > settings (EE)", () => {
     cy.findByLabelText("store icon").should("not.exist");
   });
 });
-
-function configureAuth(providerTitle) {
-  cy.findByText(providerTitle)
-    .closest(".rounded.bordered")
-    .contains("Configure")
-    .click();
-}
diff --git a/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js
index 73377dd6d2e..2694045c846 100644
--- a/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/settings/sso/google.cy.spec.js
@@ -1,45 +1,73 @@
-import { restore } from "__support__/e2e/helpers";
+import { restore, typeAndBlurUsingLabel } from "__support__/e2e/helpers";
+
+const CLIENT_ID_SUFFIX = "apps.googleusercontent.com";
 
 describe("scenarios > admin > settings > SSO > Google", () => {
   beforeEach(() => {
     restore();
     cy.signInAsAdmin();
+    cy.intercept("PUT", "/api/setting/*").as("updateSetting");
+    cy.intercept("PUT", "/api/google/settings").as("updateGoogleSettings");
+  });
+
+  it("should save the client id on subsequent tries (metabase#15974)", () => {
     cy.visit("/admin/settings/authentication/google");
+
+    typeAndBlurUsingLabel("Client ID", "example1.apps.googleusercontent.com");
+    cy.button("Save and enable").click();
+    cy.wait("@updateGoogleSettings");
+    cy.reload();
+    cy.findByDisplayValue(`example1.${CLIENT_ID_SUFFIX}`).should("be.visible");
+
+    typeAndBlurUsingLabel("Client ID", `example2.${CLIENT_ID_SUFFIX}`);
+    cy.button("Save changes").click();
+    cy.wait("@updateGoogleSettings");
+    cy.findByText("Changes saved!").should("be.visible");
   });
 
-  it("Google sign-in client ID should save on subsequent tries (metabase#15974)", () => {
-    cy.findByLabelText("Client ID").type(
-      "fake-client-id.apps.googleusercontent.com",
+  it("should disable google auth (metabase#20442)", () => {
+    setupGoogleAuth();
+    cy.visit("/admin/settings/authentication");
+
+    cy.findByRole("switch", { name: "Sign in with Google" }).click();
+    cy.wait("@updateSetting");
+    cy.findByRole("switch", { name: "Sign in with Google" }).should(
+      "not.be.checked",
     );
-    successfullySaveSettings();
-    cy.reload();
-    cy.findByDisplayValue("fake-client-id.apps.googleusercontent.com")
-      .clear()
-      .type("fake-client-id2.apps.googleusercontent.com");
-    successfullySaveSettings();
+    cy.findByText("Saved").should("exist");
   });
 
-  it("Remove Google Sing-In Setup (metabase#20442)", () => {
-    cy.request("PUT", "/api/setting", {
-      "google-auth-client-id": "example.apps.googleusercontent.com",
-      "google-auth-auto-create-accounts-domain": "example.test",
-    });
+  it("should show an error message if the client id does not end with the correct suffix (metabase#15975)", () => {
     cy.visit("/admin/settings/authentication/google");
-    cy.findByLabelText("Client ID").clear();
-    cy.findByLabelText("Domain").clear();
-    successfullySaveSettings();
-  });
 
-  it("Google sign-in client ID form should show an error message if it does not end with the correct suffix (metabase#15975)", () => {
-    cy.findByLabelText("Client ID").type("fake-client-id");
-    cy.button("Save changes").click();
+    typeAndBlurUsingLabel("Client ID", "fake-client-id");
+    cy.button("Save and enable").click();
     cy.findByText(
-      'Invalid Google Sign-In Client ID: must end with ".apps.googleusercontent.com"',
-    );
+      `Invalid Google Sign-In Client ID: must end with ".${CLIENT_ID_SUFFIX}"`,
+    ).should("be.visible");
+  });
+
+  it("should show the button to sign in via google only when enabled", () => {
+    setupGoogleAuth({ enabled: true });
+    cy.signOut();
+    cy.visit("/auth/login");
+    cy.findByText("Sign in with email").should("be.visible");
+    cy.findByRole("button", { name: /Google/ }).should("be.visible");
+
+    cy.signInAsAdmin();
+    setupGoogleAuth({ enabled: false });
+    cy.signOut();
+    cy.visit("/auth/login");
+    cy.findByText("Email address").should("be.visible");
+    cy.findByText("Password").should("be.visible");
+    cy.findByRole("button", { name: /Google/ }).should("not.exist");
   });
 });
 
-function successfullySaveSettings() {
-  cy.button("Save changes").click();
-  cy.findByText("Success");
-}
+const setupGoogleAuth = ({ enabled = true } = {}) => {
+  cy.request("PUT", "/api/google/settings", {
+    "google-auth-enabled": enabled,
+    "google-auth-client-id": `example.${CLIENT_ID_SUFFIX}`,
+    "google-auth-auto-create-accounts-domain": "example.test",
+  });
+};
-- 
GitLab