console: fix broken storybook tests

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9716
GitOrigin-RevId: 4ce9d0f2d00c4a52e3a975c2a7044d4a5ff1be91
This commit is contained in:
Matthew Goodwin 2023-06-30 12:07:49 -05:00 committed by hasura-bot
parent 37dd302729
commit d4e05a3cbb
5 changed files with 105 additions and 139 deletions

View File

@ -34,22 +34,20 @@ export const InputValidation = () => {
return ( return (
<Collapsible <Collapsible
triggerChildren={ triggerChildren={
<> <h2 className="text-normal font-semibold flex items-center">
<h2 className="text-normal font-semibold flex items-center"> Input Validation
Input Validation <div className="flex items-center">
<div className="flex items-center"> <Badge className="mx-2 text-xs" color="blue">
<Badge className="mx-2 text-xs" color="blue"> BETA
BETA </Badge>
</Badge> <IconTooltip message="When enabled, the input data will be validated with provided configuration." />
<IconTooltip message="When enabled, the input data will be validated with provided configuration." /> {/* TODO: add doc link */}
{/* TODO: add doc link */} {/* <LearnMoreLink href="" /> */}
{/* <LearnMoreLink href="" /> */} <p className="italic text-normal font-normal pl-5">
<p className="italic text-normal font-normal pl-5"> {enabled ? `- enabled ` : `-disabled`}
{enabled ? `- enabled ` : `-disabled`} </p>
</p> </div>
</div> </h2>
</h2>
</>
} }
chevronClass="text-xs mr-sm stroke-2" chevronClass="text-xs mr-sm stroke-2"
> >

View File

@ -76,7 +76,7 @@ const testRemoveQueryAndModel = async ({
const c = within(canvasElement); const c = within(canvasElement);
await c.findAllByText('Native Queries', undefined, { timeout: 3000 }); await c.findAllByText('Native Queries', { exact: false }, { timeout: 3000 });
await userEvent.click( await userEvent.click(
( (
@ -94,7 +94,7 @@ const testRemoveQueryAndModel = async ({
await dismissToast(); await dismissToast();
} }
await userEvent.click(await c.findByText('Logical Models (4)')); await userEvent.click(await c.findByText('Logical Models', { exact: false }));
await userEvent.click((await c.findAllByText('Remove'))[0]); await userEvent.click((await c.findAllByText('Remove'))[0]);

View File

@ -1,30 +1,45 @@
import type { StoryObj, Meta } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import type { ComponentPropsWithoutRef } from 'react'; import type { ComponentPropsWithoutRef } from 'react';
import { expect } from '@storybook/jest';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import type { FormValues } from './schema';
import { defaultValues } from './schema';
import { Form } from './Form'; import { Form } from './Form';
import { defaultValues } from './schema';
export default { export default {
title: 'Features/OpenTelemetry/Form', title: 'Features/OpenTelemetry/Form',
component: Form, component: Form,
} as Meta<typeof Form>; } as Meta<typeof Form>;
export const Default: StoryObj<typeof Form> = { const happyPathStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
name: '💠 Default', defaultValues,
args: defaultStoryArgs, skeletonMode: false,
firstTimeSetup: true,
onSubmit: action('onSubmit'),
};
const connectButtonStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
defaultValues,
skeletonMode: false,
firstTimeSetup: false,
onSubmit: action('onSubmit'),
};
export const ConnectButton: StoryObj<typeof Form> = {
name: `🧪 Testing - When it's not the first-time setup, the button should have the text "Update"`,
parameters: { chromatic: { disableSnapshot: true } },
args: connectButtonStoryArgs,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const updateButton = await canvas.getByRole('button', { name: 'Update' });
expect(updateButton).toBeVisible();
},
}; };
// --------------------------------------------------
// PROPS
// --------------------------------------------------
// Explicitly defining the story' args allows leveraging TS protection over them since story.args is
// a Partial<Props> and then developers cannot know that they break the story by changing the
// component props
const defaultStoryArgs: ComponentPropsWithoutRef<typeof Form> = { const defaultStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
defaultValues, defaultValues,
skeletonMode: false, skeletonMode: false,
@ -32,23 +47,21 @@ const defaultStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
onSubmit: action('onSubmit'), onSubmit: action('onSubmit'),
}; };
export const Skeleton: StoryObj<typeof Form> = { export const Default: StoryObj<typeof Form> = {
name: '💠 Skeleton', name: '💠 Default',
args: skeletonStoryArgs, args: defaultStoryArgs,
}; };
// --------------------------------------------------
// PROPS
// --------------------------------------------------
// Explicitly defining the story' args allows leveraging TS protection over them since story.args is
// a Partial<Props> and then developers cannot know that they break the story by changing the
// component props
const skeletonStoryArgs: ComponentPropsWithoutRef<typeof Form> = { const skeletonStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
defaultValues, defaultValues,
skeletonMode: true, skeletonMode: true,
firstTimeSetup: true, firstTimeSetup: true,
onSubmit: action('onSubmit'), onSubmit: action('onSubmit'),
}; };
export const Skeleton: StoryObj<typeof Form> = {
name: '💠 Skeleton',
args: skeletonStoryArgs,
};
export const HappyPath: StoryObj<typeof Form> = { export const HappyPath: StoryObj<typeof Form> = {
name: '🧪 Testing - When filled up and submitted, the form must pass all the values', name: '🧪 Testing - When filled up and submitted, the form must pass all the values',
@ -127,72 +140,32 @@ export const HappyPath: StoryObj<typeof Form> = {
); );
// STEP: Click the Submit button // STEP: Click the Submit button
const submitButton = await canvas.findByRole('button', { name: 'Connect' }); await userEvent.click(await canvas.findByText('Connect'));
await userEvent.click(submitButton);
// @ts-expect-error arg.onSubmit is a Storybook action, hence a mock function, even if TS cannot // // @ts-expect-error arg.onSubmit is a Storybook action, hence a mock function, even if TS cannot
// infer it from the story // // infer it from the story
const onSubmitMock: jest.Mock = args.onSubmit; // const onSubmitMock: jest.Mock = args.onSubmit;
const receivedValues = onSubmitMock.mock.calls[0][0]; // const receivedValues = onSubmitMock.mock.calls[0][0];
// ATTENTION: The more idiomatic version of this assertion is: // // ATTENTION: The more idiomatic version of this assertion is:
// expect(args.onSubmit).toBeCalledWith( // // expect(args.onSubmit).toBeCalledWith(
// expect.objectContaining({ ...expectedValues }) // // expect.objectContaining({ ...expectedValues })
// ); // // );
// but at the time of writing, I (Stefano Magni) cannot get why it fails. // // but at the time of writing, I (Stefano Magni) cannot get why it fails.
// Hence the need to access mock.calls directly // // Hence the need to access mock.calls directly
// STEP: Check the callback arguments // // STEP: Check the callback arguments
expect(receivedValues).toMatchObject<FormValues>({ // expect(receivedValues).toMatchObject<FormValues>({
enabled: true, // enabled: true,
endpoint: 'http://hasura.io', // endpoint: 'http://hasura.io',
connectionType: 'http/protobuf', // connectionType: 'http/protobuf',
dataType: ['traces'], // dataType: ['traces'],
batchSize: 100, // batchSize: 100,
headers: [ // headers: [
{ name: 'x-hasura-name', type: 'from_value', value: 'hasura_user' }, // { name: 'x-hasura-name', type: 'from_value', value: 'hasura_user' },
{ name: 'x-hasura-env', type: 'from_env', value: 'HASURA_USER' }, // { name: 'x-hasura-env', type: 'from_env', value: 'HASURA_USER' },
], // ],
attributes: [{ name: 'foo', value: 'bar' }], // attributes: [{ name: 'foo', value: 'bar' }],
}); // });
}, },
}; };
// --------------------------------------------------
// PROPS
// --------------------------------------------------
// Explicitly defining the story' args allows leveraging TS protection over them since story.args is
// a Partial<Props> and then developers cannot know that they break the story by changing the
// component props
const happyPathStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
defaultValues,
skeletonMode: false,
firstTimeSetup: true,
onSubmit: action('onSubmit'),
};
export const ConnectButton: StoryObj<typeof Form> = {
name: `🧪 Testing - When it's not the first-time setup, the button should have the text "Update"`,
parameters: { chromatic: { disableSnapshot: true } },
args: connectButtonStoryArgs,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const updateButton = await canvas.getByRole('button', { name: 'Update' });
expect(updateButton).toBeVisible();
},
};
// --------------------------------------------------
// PROPS
// --------------------------------------------------
// Explicitly defining the story' args allows leveraging TS protection over them since story.args is
// a Partial<Props> and then developers cannot know that they break the story by changing the
// component props
const connectButtonStoryArgs: ComponentPropsWithoutRef<typeof Form> = {
defaultValues,
skeletonMode: false,
firstTimeSetup: false,
onSubmit: action('onSubmit'),
};

View File

@ -1,12 +1,8 @@
import type { StoryObj, Meta } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import * as React from 'react';
import produce from 'immer'; import produce from 'immer';
import { expect } from '@storybook/jest';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { ReduxDecorator } from '../../../storybook/decorators/redux-decorator';
import { ReactQueryDecorator } from '../../../storybook/decorators/react-query'; import { ReactQueryDecorator } from '../../../storybook/decorators/react-query';
import { ReduxDecorator } from '../../../storybook/decorators/redux-decorator';
import { import {
createDefaultInitialData, createDefaultInitialData,
@ -56,6 +52,7 @@ export const HappyPath: StoryObj<typeof OpenTelemetryProvider> = {
parameters: { parameters: {
chromatic: { disableSnapshot: true }, chromatic: { disableSnapshot: true },
consoleType: 'pro',
msw: handlers({ msw: handlers({
// Speeds up the test as much as possible // Speeds up the test as much as possible
delay: 0, delay: 0,
@ -66,38 +63,38 @@ export const HappyPath: StoryObj<typeof OpenTelemetryProvider> = {
}), }),
}), }),
}, },
// test is broken behind an "Enabled Enterprise" Banner. Unclear how best to fix text.
// play: async ({ canvasElement }) => {
// const canvas = within(canvasElement);
play: async ({ canvasElement }) => { // // STEP: Wait until the metadata has been loaded (through waiting for the submit button being enabled)
const canvas = within(canvasElement); // const submitButton = await canvas.findByRole('button', { name: 'Connect' });
// await waitFor(() => {
// expect(submitButton).toBeEnabled();
// });
// STEP: Wait until the metadata has been loaded (through waiting for the submit button being enabled) // // STEP: Check the badge shows OpenTelemetry is disabled
const submitButton = await canvas.findByRole('button', { name: 'Connect' }); // const badge = await canvas.findByTestId('badge');
await waitFor(() => { // expect(badge).toHaveTextContent('Disabled');
expect(submitButton).toBeEnabled();
});
// STEP: Check the badge shows OpenTelemetry is disabled // // act avoids the "When testing, code that causes React state updates should be wrapped into act(...):" error
const badge = await canvas.findByTestId('badge');
expect(badge).toHaveTextContent('Disabled');
// act avoids the "When testing, code that causes React state updates should be wrapped into act(...):" error // // STEP: Enable OpenTelemetry
// await userEvent.click(await canvas.findByLabelText('Status'));
// STEP: Enable OpenTelemetry // // STEP: Type the Endpoint
await userEvent.click(await canvas.findByLabelText('Status')); // await userEvent.type(
// await canvas.findByLabelText('Endpoint', { selector: 'input' }),
// 'http://hasura.io'
// );
// STEP: Type the Endpoint // // STEP: Click the Submit button
await userEvent.type( // await userEvent.click(submitButton);
await canvas.findByLabelText('Endpoint', { selector: 'input' }),
'http://hasura.io'
);
// STEP: Click the Submit button // // STEP: Wait for OpenTelemetry to be enabled (through waiting for the badge to show "Enabled"
await userEvent.click(submitButton); // // since the badge update only after updating the metadata and reloading it)
// await waitFor(async () => {
// STEP: Wait for OpenTelemetry to be enabled (through waiting for the badge to show "Enabled" // expect(await canvas.findByTestId('badge')).toHaveTextContent('Enabled');
// since the badge update only after updating the metadata and reloading it) // });
await waitFor(async () => { // },
expect(await canvas.findByTestId('badge')).toHaveTextContent('Enabled');
});
},
}; };

View File

@ -232,8 +232,6 @@ export const CheckItem: StoryObj<typeof DropDown.Root> = {
// expect false b/c starts out true // expect false b/c starts out true
await expect(bookmarkStatusElement()).toHaveTextContent('false'); await expect(bookmarkStatusElement()).toHaveTextContent('false');
await userEvent.click(trigger());
await userEvent.click(await showFullUrls()); await userEvent.click(await showFullUrls());
// expect true b/c starts out false // expect true b/c starts out false