Skip to content
Snippets Groups Projects
Unverified Commit ec878707 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Forms Refactoring 7 — Migrate remaining forms to FormikForm (#24465)

parent 17fbdb19
No related branches found
No related tags found
No related merge requests found
Showing
with 201 additions and 161 deletions
......@@ -12,7 +12,7 @@ import Form, {
FormSubmit,
FormMessage,
FormSection,
} from "metabase/containers/Form";
} from "metabase/containers/FormikForm";
import Breadcrumbs from "metabase/components/Breadcrumbs";
import CopyWidget from "metabase/components/CopyWidget";
......
......@@ -8,7 +8,7 @@ import Form, {
FormField,
FormSubmit,
FormMessage,
} from "metabase/containers/Form";
} from "metabase/containers/FormikForm";
import { updateSettings } from "metabase/admin/settings/settings";
import { settingToFormField } from "metabase/admin/settings/utils";
......@@ -38,6 +38,7 @@ class SettingsGoogleForm extends Component {
style={{ maxWidth: 520 }}
initialValues={initialValues}
onSubmit={updateSettings}
overwriteOnInitialValuesChange
>
<Breadcrumbs
crumbs={[
......
import React, { useMemo } from "react";
import { t } from "ttag";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import { SlackSettings } from "metabase-types/api";
import { getSlackForm } from "../../forms";
import { FormProps } from "./types";
......
import React, { useCallback, useMemo } from "react";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import { SlackSettings } from "metabase-types/api";
import { getSlackForm } from "../../forms";
import { FormProps } from "./types";
......@@ -13,7 +13,11 @@ const SlackStatusForm = ({ settings }: SlackStatusFormProps): JSX.Element => {
const onSubmit = useCallback(() => undefined, []);
return (
<Form form={form} initialValues={settings} onSubmit={onSubmit}>
<Form<SlackSettings>
form={form}
initialValues={settings}
onSubmit={onSubmit}
>
{({ Form, FormField }: FormProps) => (
<Form>
<FormField name="slack-app-token" />
......
import { t } from "ttag";
import { SlackSettings } from "metabase-types/api";
import { FormObject } from "metabase-types/forms";
export const getSlackForm = (readOnly?: boolean) => ({
export const getSlackForm = (
readOnly?: boolean,
): FormObject<SlackSettings> => ({
fields: [
{
name: "slack-app-token",
......
......@@ -4,7 +4,7 @@ import PropTypes from "prop-types";
import { CSSTransitionGroup } from "react-transition-group";
import { t } from "ttag";
import Form, { FormField, FormFooter } from "metabase/containers/Form";
import Form, { FormField, FormFooter } from "metabase/containers/FormikForm";
import ModalContent from "metabase/components/ModalContent";
import Radio from "metabase/core/components/Radio";
import * as Q_DEPRECATED from "metabase/lib/query";
......@@ -25,8 +25,8 @@ export default class SaveQuestionModal extends Component {
multiStep: PropTypes.bool,
};
validateName = (name, context) => {
if (context.form.saveType.value !== "overwrite") {
validateName = (name, { values }) => {
if (values.saveType !== "overwrite") {
// We don't care if the form is valid when overwrite mode is enabled,
// as original question's data will be submitted instead of the form values
return validate.required()(name);
......
import { t } from "ttag";
import MetabaseSettings from "metabase/lib/settings";
import validate from "metabase/lib/validate";
import { PLUGIN_CACHING } from "metabase/plugins";
const FORM_FIELDS = [
{ name: "name", title: t`Name` },
{ name: "name", title: t`Name`, validate: validate.required() },
{
name: "description",
title: t`Description`,
......
......@@ -18,7 +18,7 @@ import {
import { isLocalField, isSameField } from "metabase/lib/query/field_ref";
import { isFK, getSemanticTypeIcon } from "metabase/lib/schema_metadata";
import RootForm from "metabase/containers/Form";
import RootForm from "metabase/containers/FormikForm";
import { usePrevious } from "metabase/hooks/use-previous";
import SidebarContent from "metabase/query_builder/components/SidebarContent";
......@@ -76,7 +76,7 @@ function getFormFields({ dataset }) {
value: type.id,
}));
return fieldFormValues =>
return formFieldValues =>
[
{ name: "display_name", title: t`Display name` },
{
......@@ -96,11 +96,11 @@ function getFormFields({ dataset }) {
title: t`Column type`,
widget: SemanticTypePicker,
options: getSemanticTypeOptions(),
icon: getSemanticTypeIcon(fieldFormValues.semantic_type, "ellipsis"),
icon: getSemanticTypeIcon(formFieldValues?.semantic_type, "ellipsis"),
},
{
name: "fk_target_field_id",
hidden: !isFK(fieldFormValues),
hidden: !isFK(formFieldValues),
widget: FKTargetPicker,
databaseId: dataset.databaseId(),
},
......
import React, { useState } from "react";
import { t } from "ttag";
import Users from "metabase/entities/users";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import { SubscribeInfo } from "metabase-types/store";
import {
FormContainer,
......@@ -47,7 +47,7 @@ const NewsletterForm = ({
{t`Get infrequent emails about new releases and feature updates.`}
</FormHeader>
{!isSubscribed && (
<Form
<Form<{ email: string }>
form={Users.forms.newsletter}
initialValues={initialValues}
submitTitle={t`Subscribe`}
......
......@@ -9,7 +9,7 @@ const FormMock = (props: FormHTMLAttributes<HTMLFormElement>) => (
</form>
);
jest.mock("metabase/containers/Form", () => FormMock);
jest.mock("metabase/containers/FormikForm", () => FormMock);
jest.mock("metabase/entities/users", () => ({
forms: { newsletter: jest.fn() },
......
import React, { useCallback, useMemo } from "react";
import { t } from "ttag";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import forms from "metabase/entities/timeline-events/forms";
import { Timeline, TimelineEvent } from "metabase-types/api";
import ModalBody from "../ModalBody";
......@@ -47,7 +47,7 @@ const EditEventModal = ({
<div>
<ModalHeader title={t`Edit event`} onClose={onClose} />
<ModalBody>
<Form
<Form<TimelineEvent>
form={form}
initialValues={event}
isModal={true}
......
......@@ -13,7 +13,7 @@ const FormMock = (props: FormHTMLAttributes<HTMLFormElement>) => (
</form>
);
jest.mock("metabase/containers/Form", () => FormMock);
jest.mock("metabase/containers/FormikForm", () => FormMock);
describe("EditEventModal", () => {
it("should submit modal", () => {
......
import React, { useCallback, useMemo } from "react";
import { t } from "ttag";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import forms from "metabase/entities/timelines/forms";
import { Timeline } from "metabase-types/api";
import ModalBody from "../ModalBody";
......
......@@ -10,7 +10,7 @@ const FormMock = (props: FormHTMLAttributes<HTMLFormElement>) => (
</form>
);
jest.mock("metabase/containers/Form", () => FormMock);
jest.mock("metabase/containers/FormikForm", () => FormMock);
describe("EditTimelineModal", () => {
it("should submit modal", () => {
......
......@@ -2,7 +2,7 @@ import React, { useCallback, useMemo } from "react";
import { t } from "ttag";
import { getDefaultTimezone } from "metabase/lib/time";
import { getDefaultTimelineIcon } from "metabase/lib/timelines";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import forms from "metabase/entities/timeline-events/forms";
import {
Collection,
......@@ -51,7 +51,7 @@ const NewEventModal = ({
const hasOneTimeline = availableTimelines.length === 1;
return {
timeline_id: defaultTimeline ? defaultTimeline.id : null,
timeline_id: defaultTimeline ? defaultTimeline.id : undefined,
icon: hasOneTimeline ? defaultTimeline.icon : getDefaultTimelineIcon(),
timezone: getDefaultTimezone(),
source,
......@@ -73,7 +73,7 @@ const NewEventModal = ({
<div>
<ModalHeader title={t`New event`} onClose={onClose} />
<ModalBody>
<Form
<Form<Partial<TimelineEvent>>
form={form}
initialValues={initialValues}
isModal={true}
......
import React, { useCallback, useMemo } from "react";
import { t } from "ttag";
import Form from "metabase/containers/Form";
import Form from "metabase/containers/FormikForm";
import forms from "metabase/entities/timelines/forms";
import { getDefaultTimelineIcon } from "metabase/lib/timelines";
import { canonicalCollectionId } from "metabase/collections/utils";
......
......@@ -10,7 +10,7 @@ const FormMock = (props: FormHTMLAttributes<HTMLFormElement>) => (
</form>
);
jest.mock("metabase/containers/Form", () => FormMock);
jest.mock("metabase/containers/FormikForm", () => FormMock);
describe("NewTimelineModal", () => {
it("should submit modal", () => {
......
import React from "react";
import { renderWithProviders, screen } from "__support__/ui";
import { act, renderWithProviders, screen, waitFor } from "__support__/ui";
import userEvent from "@testing-library/user-event";
import mock from "xhr-mock";
......@@ -37,7 +37,7 @@ function mockCachingEnabled(enabled = true) {
});
}
const renderSaveQuestionModal = (question, originalQuestion) => {
const setup = async (question, originalQuestion) => {
const onCreateMock = jest.fn(() => Promise.resolve());
const onSaveMock = jest.fn(() => Promise.resolve());
const onCloseMock = jest.fn();
......@@ -51,6 +51,7 @@ const renderSaveQuestionModal = (question, originalQuestion) => {
onClose={onCloseMock}
/>,
);
await waitFor(() => screen.getByRole("button", { name: "Save" }));
return { onSaveMock, onCreateMock, onCloseMock };
};
......@@ -60,7 +61,7 @@ function getQuestion({
isSaved,
name = "Q1",
description = "Example",
collection_id = 12,
collection_id = null,
can_write = true,
} = {}) {
const extraCardParams = {};
......@@ -106,20 +107,25 @@ function getDirtyQuestion(originalQuestion) {
});
}
function fillForm({ name, description }) {
async function fillForm({ name, description }) {
if (name) {
const input = screen.getByLabelText("Name");
userEvent.clear(input);
userEvent.type(input, name);
await userEvent.clear(input);
await userEvent.type(input, name);
}
if (description) {
const input = screen.getByLabelText("Description");
userEvent.clear(input);
userEvent.type(input, description);
await userEvent.clear(input);
await userEvent.type(input, description);
}
}
describe("SaveQuestionModal", () => {
beforeAll(() => {
console.error = jest.fn();
console.warn = jest.fn();
});
const TEST_COLLECTIONS = [
{
can_write: false,
......@@ -148,6 +154,9 @@ describe("SaveQuestionModal", () => {
mock.get("/api/collection", {
body: JSON.stringify(TEST_COLLECTIONS),
});
mock.get("/api/collection/root", {
body: JSON.stringify(TEST_COLLECTIONS)[0],
});
});
afterEach(() => {
......@@ -155,15 +164,15 @@ describe("SaveQuestionModal", () => {
});
describe("new question", () => {
it("should suggest a name for structured queries", () => {
renderSaveQuestionModal(getQuestion());
it("should suggest a name for structured queries", async () => {
await setup(getQuestion());
expect(screen.getByLabelText("Name")).toHaveValue(
EXPECTED_SUGGESTED_NAME,
);
});
it("should not suggest a name for native queries", () => {
renderSaveQuestionModal(
it("should not suggest a name for native queries", async () => {
await setup(
new Question(
{
dataset_query: {
......@@ -182,16 +191,18 @@ describe("SaveQuestionModal", () => {
expect(screen.getByLabelText("Name")).toHaveValue("");
});
it("should display empty description input", () => {
renderSaveQuestionModal(getQuestion());
it("should display empty description input", async () => {
await setup(getQuestion());
expect(screen.getByLabelText("Description")).toHaveValue("");
});
it("should call onCreate correctly with default form values", () => {
it("should call onCreate correctly with default form values", async () => {
const question = getQuestion();
const { onCreateMock } = renderSaveQuestionModal(question);
const { onCreateMock } = await setup(question);
userEvent.click(screen.getByText("Save"));
await act(async () => {
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -202,12 +213,17 @@ describe("SaveQuestionModal", () => {
});
});
it("should call onCreate correctly with edited form", () => {
it("should call onCreate correctly with edited form", async () => {
const question = getQuestion();
const { onCreateMock } = renderSaveQuestionModal(question);
fillForm({ name: "My favorite orders", description: "So many of them" });
userEvent.click(screen.getByText("Save"));
const { onCreateMock } = await setup(question);
await act(async () => {
await fillForm({
name: "My favorite orders",
description: "So many of them",
});
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -218,15 +234,17 @@ describe("SaveQuestionModal", () => {
});
});
it("should trim name and description", () => {
it("should trim name and description", async () => {
const question = getQuestion();
const { onCreateMock } = renderSaveQuestionModal(question);
fillForm({
name: " My favorite orders ",
description: " So many of them ",
const { onCreateMock } = await setup(question);
await act(async () => {
await fillForm({
name: " My favorite orders ",
description: " So many of them ",
});
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
userEvent.click(screen.getByText("Save"));
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -237,14 +255,16 @@ describe("SaveQuestionModal", () => {
});
});
it('should correctly handle saving a question in the "root" collection', () => {
it('should correctly handle saving a question in the "root" collection', async () => {
const question = getQuestion({
collection_id: "root",
});
const { onCreateMock } = renderSaveQuestionModal(question);
const { onCreateMock } = await setup(question);
fillForm({ name: "foo", description: "bar" });
userEvent.click(screen.getByText("Save"));
await act(async () => {
await fillForm({ name: "foo", description: "bar" });
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -255,17 +275,19 @@ describe("SaveQuestionModal", () => {
});
});
it("shouldn't call onSave when form is submitted", () => {
it("shouldn't call onSave when form is submitted", async () => {
const question = getQuestion();
const { onSaveMock } = renderSaveQuestionModal(question);
const { onSaveMock } = await setup(question);
userEvent.click(screen.getByText("Save"));
await act(async () => {
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onSaveMock).not.toHaveBeenCalled();
});
it("shouldn't show a control to overwrite a saved question", () => {
renderSaveQuestionModal(getQuestion());
it("shouldn't show a control to overwrite a saved question", async () => {
await setup(getQuestion());
expect(
screen.queryByText("Save as new question"),
).not.toBeInTheDocument();
......@@ -276,12 +298,9 @@ describe("SaveQuestionModal", () => {
});
describe("saving as a new question", () => {
it("should offer to replace the original question by default", () => {
it("should offer to replace the original question by default", async () => {
const originalQuestion = getQuestion({ isSaved: true });
renderSaveQuestionModal(
getDirtyQuestion(originalQuestion),
originalQuestion,
);
await setup(getDirtyQuestion(originalQuestion), originalQuestion);
expect(
screen.getByLabelText(/Replace original question, ".*"/),
......@@ -289,7 +308,7 @@ describe("SaveQuestionModal", () => {
expect(screen.getByText("Save as new question")).not.toBeChecked();
});
it("should switch to the new question form", () => {
it("should switch to the new question form", async () => {
const CARD = {
name: "Q1",
description: "Example description",
......@@ -297,9 +316,11 @@ describe("SaveQuestionModal", () => {
};
const originalQuestion = getQuestion({ isSaved: true, ...CARD });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
renderSaveQuestionModal(dirtyQuestion, originalQuestion);
await setup(dirtyQuestion, originalQuestion);
userEvent.click(screen.getByText("Save as new question"));
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
});
expect(screen.getByLabelText("Name")).toHaveValue(
EXPECTED_DIRTY_SUGGESTED_NAME,
......@@ -310,16 +331,16 @@ describe("SaveQuestionModal", () => {
expect(screen.queryByText("Our analytics")).toBeInTheDocument();
});
it("should allow to save a question with default form values", () => {
// one
it("should allow to save a question with default form values", async () => {
const originalQuestion = getQuestion({ isSaved: true });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
const { onCreateMock } = renderSaveQuestionModal(
dirtyQuestion,
originalQuestion,
);
const { onCreateMock } = await setup(dirtyQuestion, originalQuestion);
userEvent.click(screen.getByText("Save as new question"));
userEvent.click(screen.getByRole("button", { name: "Save" }));
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -328,17 +349,16 @@ describe("SaveQuestionModal", () => {
});
});
it("show allow to save a question with an edited form", () => {
it("show allow to save a question with an edited form", async () => {
const originalQuestion = getQuestion({ isSaved: true });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
const { onCreateMock } = renderSaveQuestionModal(
dirtyQuestion,
originalQuestion,
);
const { onCreateMock } = await setup(dirtyQuestion, originalQuestion);
userEvent.click(screen.getByText("Save as new question"));
fillForm({ name: "My Q", description: "Sample" });
userEvent.click(screen.getByRole("button", { name: "Save" }));
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await fillForm({ name: "My Q", description: "Sample" });
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).toHaveBeenCalledTimes(1);
expect(onCreateMock).toHaveBeenCalledWith({
......@@ -348,44 +368,42 @@ describe("SaveQuestionModal", () => {
});
});
it("shouldn't allow to save a question if form is invalid", () => {
it("shouldn't allow to save a question if form is invalid", async () => {
const originalQuestion = getQuestion({ isSaved: true });
renderSaveQuestionModal(
getDirtyQuestion(originalQuestion),
originalQuestion,
);
await setup(getDirtyQuestion(originalQuestion), originalQuestion);
userEvent.click(screen.getByText("Save as new question"));
userEvent.clear(screen.getByLabelText("Name"));
userEvent.clear(screen.getByLabelText("Description"));
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await userEvent.clear(screen.getByLabelText("Name"));
await userEvent.clear(screen.getByLabelText("Description"));
});
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
});
});
describe("overwriting a saved question", () => {
it("should display original question's name on save mode control", () => {
it("should display original question's name on save mode control", async () => {
const originalQuestion = getQuestion({
isSaved: true,
name: "Beautiful Orders",
});
const dirtyQuestion = getDirtyQuestion(originalQuestion);
renderSaveQuestionModal(dirtyQuestion, originalQuestion);
await setup(dirtyQuestion, originalQuestion);
expect(
screen.queryByText('Replace original question, "Beautiful Orders"'),
).toBeInTheDocument();
});
it("should call onSave correctly when form is submitted", () => {
it("should call onSave correctly when form is submitted", async () => {
const originalQuestion = getQuestion({ isSaved: true });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
const { onSaveMock } = renderSaveQuestionModal(
dirtyQuestion,
originalQuestion,
);
const { onSaveMock } = await setup(dirtyQuestion, originalQuestion);
userEvent.click(screen.getByText("Save"));
await act(async () => {
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onSaveMock).toHaveBeenCalledTimes(1);
expect(onSaveMock).toHaveBeenCalledWith({
......@@ -394,17 +412,18 @@ describe("SaveQuestionModal", () => {
});
});
it("should allow switching to 'save as new' and back", () => {
it("should allow switching to 'save as new' and back", async () => {
const originalQuestion = getQuestion({ isSaved: true });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
const { onSaveMock } = renderSaveQuestionModal(
dirtyQuestion,
originalQuestion,
);
userEvent.click(screen.getByText("Save as new question"));
userEvent.click(screen.getByText(/Replace original question, ".*"/));
userEvent.click(screen.getByText("Save"));
const { onSaveMock } = await setup(dirtyQuestion, originalQuestion);
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await userEvent.click(
screen.getByText(/Replace original question, ".*"/),
);
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onSaveMock).toHaveBeenCalledTimes(1);
expect(onSaveMock).toHaveBeenCalledWith({
......@@ -413,17 +432,19 @@ describe("SaveQuestionModal", () => {
});
});
it("should preserve original question's collection id", () => {
it("should preserve original question's collection id", async () => {
const originalQuestion = getQuestion({
isSaved: true,
collection_id: 5,
});
const { onSaveMock } = renderSaveQuestionModal(
const { onSaveMock } = await setup(
getDirtyQuestion(originalQuestion),
originalQuestion,
);
userEvent.click(screen.getByText("Save"));
await act(async () => {
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onSaveMock).toHaveBeenCalledWith(
expect.objectContaining({
......@@ -432,42 +453,44 @@ describe("SaveQuestionModal", () => {
);
});
it("shouldn't allow to save a question if form is invalid", () => {
renderSaveQuestionModal(getQuestion());
it("shouldn't allow to save a question if form is invalid", async () => {
await setup(getQuestion());
userEvent.clear(screen.getByLabelText("Name"));
userEvent.clear(screen.getByLabelText("Description"));
await act(async () => {
await userEvent.clear(screen.getByLabelText("Name"));
await userEvent.clear(screen.getByLabelText("Description"));
});
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
});
it("shouldn't call onCreate when form is submitted", () => {
it("shouldn't call onCreate when form is submitted", async () => {
const originalQuestion = getQuestion({ isSaved: true });
const dirtyQuestion = getDirtyQuestion(originalQuestion);
const { onCreateMock } = renderSaveQuestionModal(
dirtyQuestion,
originalQuestion,
);
const { onCreateMock } = await setup(dirtyQuestion, originalQuestion);
userEvent.click(screen.getByText("Save"));
await act(async () => {
await userEvent.click(screen.getByRole("button", { name: "Save" }));
});
expect(onCreateMock).not.toHaveBeenCalled();
});
it("should keep 'save as new' form values while switching saving modes", () => {
it("should keep 'save as new' form values while switching saving modes", async () => {
const originalQuestion = getQuestion({ isSaved: true });
renderSaveQuestionModal(
getDirtyQuestion(originalQuestion),
originalQuestion,
);
userEvent.click(screen.getByText("Save as new question"));
fillForm({
name: "Should not be erased",
description: "This should not be erased too",
await setup(getDirtyQuestion(originalQuestion), originalQuestion);
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await fillForm({
name: "Should not be erased",
description: "This should not be erased too",
});
await userEvent.click(
screen.getByText(/Replace original question, ".*"/),
);
await userEvent.click(screen.getByText("Save as new question"));
});
userEvent.click(screen.getByText(/Replace original question, ".*"/));
userEvent.click(screen.getByText("Save as new question"));
expect(screen.getByLabelText("Name")).toHaveValue("Should not be erased");
expect(screen.getByLabelText("Description")).toHaveValue(
......@@ -475,28 +498,29 @@ describe("SaveQuestionModal", () => {
);
});
it("should allow to replace the question if new question form is invalid (metabase#13817", () => {
it("should allow to replace the question if new question form is invalid (metabase#13817)", async () => {
const originalQuestion = getQuestion({ isSaved: true });
renderSaveQuestionModal(
getDirtyQuestion(originalQuestion),
originalQuestion,
);
userEvent.click(screen.getByText("Save as new question"));
userEvent.clear(screen.getByLabelText("Name"));
userEvent.click(screen.getByText(/Replace original question, ".*"/));
await setup(getDirtyQuestion(originalQuestion), originalQuestion);
await act(async () => {
await userEvent.click(screen.getByText("Save as new question"));
await userEvent.clear(screen.getByLabelText("Name"));
await userEvent.click(
screen.getByText(/Replace original question, ".*"/),
);
});
expect(screen.getByRole("button", { name: "Save" })).toBeEnabled();
expect(await screen.getByRole("button", { name: "Save" })).toBeEnabled();
});
it("should not allow overwriting when user does not have curate permission on collection (metabase#20717)", () => {
it("should not allow overwriting when user does not have curate permission on collection (metabase#20717)", async () => {
const originalQuestion = getQuestion({
isSaved: true,
name: "Beautiful Orders",
can_write: false,
});
const dirtyQuestion = getDirtyQuestion(originalQuestion);
renderSaveQuestionModal(dirtyQuestion, originalQuestion);
await setup(dirtyQuestion, originalQuestion);
expect(
screen.queryByText("Save as new question"),
......@@ -507,15 +531,19 @@ describe("SaveQuestionModal", () => {
});
});
it("should call onClose when Cancel button is clicked", () => {
const { onCloseMock } = renderSaveQuestionModal(getQuestion());
userEvent.click(screen.getByRole("button", { name: "Cancel" }));
it("should call onClose when Cancel button is clicked", async () => {
const { onCloseMock } = await setup(getQuestion());
await act(async () => {
userEvent.click(screen.getByRole("button", { name: "Cancel" }));
});
expect(onCloseMock).toHaveBeenCalledTimes(1);
});
it("should call onClose when close icon is clicked", () => {
const { onCloseMock } = renderSaveQuestionModal(getQuestion());
userEvent.click(screen.getByLabelText("close icon"));
it("should call onClose when close icon is clicked", async () => {
const { onCloseMock } = await setup(getQuestion());
await act(async () => {
userEvent.click(screen.getByLabelText("close icon"));
});
expect(onCloseMock).toHaveBeenCalledTimes(1);
});
......@@ -534,8 +562,8 @@ describe("SaveQuestionModal", () => {
.question();
describe("OSS", () => {
it("is not shown", () => {
renderSaveQuestionModal(question);
it("is not shown", async () => {
await setup(question);
expect(screen.queryByText("More options")).not.toBeInTheDocument();
expect(
screen.queryByText("Cache all question results for"),
......@@ -548,8 +576,8 @@ describe("SaveQuestionModal", () => {
setupEnterpriseTest();
});
it("is not shown", () => {
renderSaveQuestionModal(question);
it("is not shown", async () => {
await setup(question);
expect(screen.queryByText("More options")).not.toBeInTheDocument();
expect(
screen.queryByText("Cache all question results for"),
......
......@@ -19,7 +19,7 @@ export function mapColumnTo({ table, column } = {}) {
cy.findByText("Database column this maps to")
.closest(".Form-field")
.findByTestId("select-button")
.click();
.click({ force: true });
popover().contains(table).click();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment