superset/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.test.tsx

214 lines
7.9 KiB
TypeScript

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { getChartMetadataRegistry, ChartMetadata } from '@superset-ui/core';
import fetchMock from 'fetch-mock';
import setupColors from 'src/setup/setupColors';
import { ANNOTATION_TYPES_METADATA } from 'src/modules/AnnotationTypes';
import AnnotationLayer from './AnnotationLayer';
const defaultProps = {
value: '',
vizType: 'table',
annotationType: ANNOTATION_TYPES_METADATA.FORMULA.value,
};
beforeAll(() => {
const supportedAnnotationTypes = Object.values(ANNOTATION_TYPES_METADATA).map(
value => value.value,
);
fetchMock.get('glob:*/annotationlayermodelview/api/read?*', {
result: [{ label: 'Chart A', value: 'a' }],
});
fetchMock.get('glob:*/superset/user_slices*', [
{ id: 'a', title: 'Chart A', viz_type: 'table', data: {} },
]);
setupColors();
getChartMetadataRegistry().registerValue(
'table',
new ChartMetadata({
name: 'Table',
thumbnail: '',
supportedAnnotationTypes,
canBeAnnotationTypes: ['EVENT'],
}),
);
});
const waitForRender = (props?: any) =>
waitFor(() => render(<AnnotationLayer {...defaultProps} {...props} />));
test('renders with default props', async () => {
await waitForRender();
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Cancel' })).toBeEnabled();
});
test('renders extra checkboxes when type is time series', async () => {
await waitForRender();
expect(
screen.queryByRole('button', { name: 'Show Markers' }),
).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: 'Hide Line' }),
).not.toBeInTheDocument();
userEvent.click(screen.getAllByText('Formula')[0]);
userEvent.click(screen.getByText('Time series'));
expect(
screen.getByRole('button', { name: 'Show Markers' }),
).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Hide Line' })).toBeInTheDocument();
});
test('enables apply and ok buttons', async () => {
await waitForRender();
userEvent.type(screen.getByLabelText('Name'), 'Test');
userEvent.type(screen.getByLabelText('Formula'), '2x');
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
});
});
test('triggers addAnnotationLayer when apply button is clicked', async () => {
const addAnnotationLayer = jest.fn();
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer });
userEvent.click(screen.getByRole('button', { name: 'Apply' }));
expect(addAnnotationLayer).toHaveBeenCalled();
});
test('triggers addAnnotationLayer and close when ok button is clicked', async () => {
const addAnnotationLayer = jest.fn();
const close = jest.fn();
await waitForRender({ name: 'Test', value: '2x', addAnnotationLayer, close });
userEvent.click(screen.getByRole('button', { name: 'OK' }));
expect(addAnnotationLayer).toHaveBeenCalled();
expect(close).toHaveBeenCalled();
});
test('triggers close when cancel button is clicked', async () => {
const close = jest.fn();
await waitForRender({ close });
userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
expect(close).toHaveBeenCalled();
});
test('triggers removeAnnotationLayer and close when remove button is clicked', async () => {
const removeAnnotationLayer = jest.fn();
const close = jest.fn();
await waitForRender({
name: 'Test',
value: '2x',
removeAnnotationLayer,
close,
});
userEvent.click(screen.getByRole('button', { name: 'Remove' }));
expect(removeAnnotationLayer).toHaveBeenCalled();
expect(close).toHaveBeenCalled();
});
test('renders chart options', async () => {
await waitForRender({
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
});
userEvent.click(screen.getByText('2 option(s)'));
userEvent.click(screen.getByText('Superset annotation'));
expect(await screen.findByLabelText('Annotation layer')).toBeInTheDocument();
userEvent.click(screen.getByText('Superset annotation'));
userEvent.click(screen.getByText('Table'));
expect(await screen.findByLabelText('Chart')).toBeInTheDocument();
});
test('keeps apply disabled when missing required fields', async () => {
await waitForRender({
annotationType: ANNOTATION_TYPES_METADATA.EVENT.value,
sourceType: 'Table',
});
userEvent.click(screen.getByText('1 option(s)'));
await waitFor(() => userEvent.click(screen.getByText('Chart A')));
expect(
screen.getByText('Annotation Slice Configuration'),
).toBeInTheDocument();
userEvent.click(screen.getByRole('button', { name: 'Automatic Color' }));
userEvent.click(screen.getByLabelText('Title Column'));
userEvent.click(screen.getByText('None'));
userEvent.click(screen.getByLabelText('Style'));
userEvent.click(screen.getByText('Dashed'));
userEvent.click(screen.getByLabelText('Opacity'));
userEvent.click(screen.getByText('0.5'));
const checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => userEvent.click(checkbox));
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
});
test.skip('Disable apply button if formula is incorrect', async () => {
// TODO: fix flaky test that passes locally but fails on CI
await waitForRender({ name: 'test' });
userEvent.clear(screen.getByLabelText('Formula'));
userEvent.type(screen.getByLabelText('Formula'), 'x+1');
await waitFor(() => {
expect(screen.getByLabelText('Formula')).toHaveValue('x+1');
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
});
userEvent.clear(screen.getByLabelText('Formula'));
userEvent.type(screen.getByLabelText('Formula'), 'y = x*2+1');
await waitFor(() => {
expect(screen.getByLabelText('Formula')).toHaveValue('y = x*2+1');
expect(screen.getByRole('button', { name: 'OK' })).toBeEnabled();
expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled();
});
userEvent.clear(screen.getByLabelText('Formula'));
userEvent.type(screen.getByLabelText('Formula'), 'y+1');
await waitFor(() => {
expect(screen.getByLabelText('Formula')).toHaveValue('y+1');
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
});
userEvent.clear(screen.getByLabelText('Formula'));
userEvent.type(screen.getByLabelText('Formula'), 'x+');
await waitFor(() => {
expect(screen.getByLabelText('Formula')).toHaveValue('x+');
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
});
userEvent.clear(screen.getByLabelText('Formula'));
userEvent.type(screen.getByLabelText('Formula'), 'y = z+1');
await waitFor(() => {
expect(screen.getByLabelText('Formula')).toHaveValue('y = z+1');
expect(screen.getByRole('button', { name: 'OK' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled();
});
});