mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-08 21:48:22 +00:00
Enhance Bulk Edit Entity tests with response handling and update MySQL ingestion class to exclude specific schemas. Refactor glossary utility functions for improved visibility checks. (#20837)
* Enhance Bulk Edit Entity tests with response handling and update MySQL ingestion class to exclude specific schemas. Refactor glossary utility functions for improved visibility checks. * Refactor ImageAttachment component to improve loading state handling and update tests for better coverage. Adjust Bulk Edit Entity tests to streamline response handling and fix selector usage in GlossaryVersionPage tests. * revert framenavigated wait from table
This commit is contained in:
parent
fb5af8ad7c
commit
be716153ee
@ -161,11 +161,17 @@ test.describe('Bulk Edit Entity', () => {
|
||||
failed: '0',
|
||||
});
|
||||
|
||||
const updateButtonResponse = page.waitForResponse(
|
||||
`/api/v1/services/databaseServices/name/*/importAsync?*dryRun=false&recursive=false*`
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await page
|
||||
.locator('.inovua-react-toolkit-load-mask__background-layer')
|
||||
.waitFor({ state: 'detached' });
|
||||
|
||||
await updateButtonResponse;
|
||||
await page.waitForEvent('framenavigated');
|
||||
await toastNotification(page, /details updated successfully/);
|
||||
|
||||
await page.click('[data-testid="databases"]');
|
||||
@ -311,12 +317,15 @@ test.describe('Bulk Edit Entity', () => {
|
||||
await page.waitForSelector('.InovuaReactDataGrid__header-layout', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
const updateButtonResponse = page.waitForResponse(
|
||||
`/api/v1/databases/name/*/importAsync?*dryRun=false&recursive=false*`
|
||||
);
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
await page
|
||||
.locator('.inovua-react-toolkit-load-mask__background-layer')
|
||||
.waitFor({ state: 'detached' });
|
||||
|
||||
await updateButtonResponse;
|
||||
await page.waitForEvent('framenavigated');
|
||||
await toastNotification(page, /details updated successfully/);
|
||||
|
||||
// Verify Details updated
|
||||
@ -443,8 +452,13 @@ test.describe('Bulk Edit Entity', () => {
|
||||
processed: '2',
|
||||
failed: '0',
|
||||
});
|
||||
|
||||
const updateButtonResponse = page.waitForResponse(
|
||||
`/api/v1/databaseSchemas/name/*/importAsync?*dryRun=false&recursive=false*`
|
||||
);
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await updateButtonResponse;
|
||||
await page.waitForEvent('framenavigated');
|
||||
await toastNotification(page, /details updated successfully/);
|
||||
|
||||
// Verify Details updated
|
||||
@ -561,6 +575,9 @@ test.describe('Bulk Edit Entity', () => {
|
||||
failed: '0',
|
||||
});
|
||||
|
||||
const updateButtonResponse = page.waitForResponse(
|
||||
`/api/v1/tables/name/*/importAsync?*dryRun=false&recursive=false*`
|
||||
);
|
||||
await page.click('[type="button"] >> text="Update"', { force: true });
|
||||
await page
|
||||
.locator('.inovua-react-toolkit-load-mask__background-layer')
|
||||
@ -569,7 +586,7 @@ test.describe('Bulk Edit Entity', () => {
|
||||
await page.waitForSelector('.message-banner-wrapper', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await updateButtonResponse;
|
||||
await toastNotification(page, /details updated successfully/);
|
||||
|
||||
// Verify Details updated
|
||||
|
||||
@ -204,7 +204,7 @@ test('GlossaryTerm', async ({ page }) => {
|
||||
const glossaryTermsRes = page.waitForResponse(
|
||||
'/api/v1/glossaryTerms/name/**'
|
||||
);
|
||||
await page.click('[data-testid="version-button"]');
|
||||
await page.getByRole('dialog').getByRole('img').click();
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
await glossaryTermsRes;
|
||||
|
||||
@ -37,6 +37,7 @@ class MysqlIngestionClass extends ServiceBaseClass {
|
||||
name = '';
|
||||
defaultFilters = ['^information_schema$', '^performance_schema$'];
|
||||
tableFilter: string[];
|
||||
excludeSchemas: string[];
|
||||
profilerTable = 'alert_entity';
|
||||
constructor(tableFilter?: string[]) {
|
||||
const serviceName = `pw-mysql-with-%-${uuid()}`;
|
||||
@ -47,6 +48,7 @@ class MysqlIngestionClass extends ServiceBaseClass {
|
||||
'alert_entity',
|
||||
'chart_entity',
|
||||
];
|
||||
this.excludeSchemas = ['openmetadata'];
|
||||
}
|
||||
|
||||
async createService(page: Page) {
|
||||
@ -77,6 +79,12 @@ class MysqlIngestionClass extends ServiceBaseClass {
|
||||
.locator('#root\\/tableFilterPattern\\/includes')
|
||||
.press('Enter');
|
||||
}
|
||||
for (const schema of this.excludeSchemas) {
|
||||
await page.fill('#root\\/schemaFilterPattern\\/excludes', schema);
|
||||
await page
|
||||
.locator('#root\\/schemaFilterPattern\\/excludes')
|
||||
.press('Enter');
|
||||
}
|
||||
}
|
||||
|
||||
async runAdditionalTests(
|
||||
@ -187,7 +195,7 @@ class MysqlIngestionClass extends ServiceBaseClass {
|
||||
await page.waitForSelector('.ant-select-selection-item-content');
|
||||
|
||||
await expect(page.locator('.ant-select-selection-item-content')).toHaveText(
|
||||
this.defaultFilters.concat(this.tableFilter)
|
||||
this.defaultFilters.concat([...this.excludeSchemas, ...this.tableFilter])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1106,11 +1106,10 @@ export const approveTagsTask = async (
|
||||
await selectActiveGlossary(page, entity.data.displayName);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const tagVisibility = await page.isVisible(
|
||||
`[data-testid="tag-${value.tag}"]`
|
||||
);
|
||||
const tagVisibility = page.locator(`[data-testid="tag-${value.tag}"]`);
|
||||
await tagVisibility.scrollIntoViewIfNeeded();
|
||||
|
||||
expect(tagVisibility).toBe(true);
|
||||
await expect(tagVisibility).toBeVisible();
|
||||
};
|
||||
|
||||
export async function openColumnDropdown(page: Page): Promise<void> {
|
||||
|
||||
@ -39,7 +39,7 @@ export const createGlossaryTermRowDetails = () => {
|
||||
};
|
||||
|
||||
export const fillTextInputDetails = async (page: Page, text: string) => {
|
||||
await page.locator('.InovuaReactDataGrid__cell--cell-active').press('Enter');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.locator('.ant-layout-content').getByRole('textbox').fill(text);
|
||||
await page
|
||||
@ -69,6 +69,8 @@ export const fillOwnerDetails = async (page: Page, owners: string[]) => {
|
||||
.locator('.InovuaReactDataGrid__cell--cell-active')
|
||||
.press('Enter', { delay: 100 });
|
||||
|
||||
await expect(page.getByTestId('select-owner-tabs')).toBeVisible();
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { NodeViewProps } from '@tiptap/react';
|
||||
import React from 'react';
|
||||
import { UPLOADED_ASSETS_URL } from '../../../../../constants/BlockEditor.constants';
|
||||
import ImageAttachment from './ImageAttachment';
|
||||
|
||||
describe('ImageAttachment', () => {
|
||||
@ -28,24 +29,7 @@ describe('ImageAttachment', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render loading state when isMediaLoading is true and needs authentication', () => {
|
||||
const authenticatedNode = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
url: '/api/v1/attachments/123',
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
render(
|
||||
<ImageAttachment isMediaLoading mediaSrc="" node={authenticatedNode} />
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
||||
expect(screen.getByText('label.loading')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render uploading state when isUploading is true', () => {
|
||||
it('should render loading state when isUploading is true', () => {
|
||||
const uploadingNode = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
@ -62,8 +46,29 @@ describe('ImageAttachment', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
||||
expect(screen.getByText('label.uploading')).toBeInTheDocument();
|
||||
const imageContainer = screen.getByTestId('image-container');
|
||||
|
||||
expect(imageContainer).toHaveClass('loading-state');
|
||||
expect(screen.queryByTestId('uploaded-image-node')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render loading state when media is loading and needs authentication', () => {
|
||||
const authenticatedNode = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
url: `${UPLOADED_ASSETS_URL}/123`,
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
render(
|
||||
<ImageAttachment isMediaLoading mediaSrc="" node={authenticatedNode} />
|
||||
);
|
||||
|
||||
const imageContainer = screen.getByTestId('image-container');
|
||||
|
||||
expect(imageContainer).toHaveClass('loading-state');
|
||||
expect(screen.queryByTestId('uploaded-image-node')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render image when mediaSrc is provided', async () => {
|
||||
@ -85,20 +90,13 @@ describe('ImageAttachment', () => {
|
||||
|
||||
it('should show error state when image fails to load', async () => {
|
||||
render(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
mediaSrc="invalid-url"
|
||||
node={mockNode}
|
||||
/>
|
||||
<ImageAttachment isMediaLoading={false} mediaSrc="" node={mockNode} />
|
||||
);
|
||||
|
||||
const image = screen.getByTestId('uploaded-image-node');
|
||||
fireEvent.error(image);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('uploaded-image-node')).toHaveStyle({
|
||||
visibility: 'hidden',
|
||||
});
|
||||
expect(
|
||||
screen.queryByTestId('uploaded-image-node')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@ -107,23 +105,82 @@ describe('ImageAttachment', () => {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
url: '/api/v1/attachments/123',
|
||||
url: `${UPLOADED_ASSETS_URL}/123`,
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
render(
|
||||
<ImageAttachment isMediaLoading mediaSrc="" node={authenticatedNode} />
|
||||
);
|
||||
|
||||
const imageContainer = screen.getByTestId('image-container');
|
||||
|
||||
expect(imageContainer).toHaveClass('loading-state');
|
||||
expect(screen.queryByTestId('uploaded-image-node')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display authenticated image when mediaSrc is provided', async () => {
|
||||
const authenticatedNode = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
url: `${UPLOADED_ASSETS_URL}/123`,
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
const mediaSrc = 'https://example.com/authenticated-image.jpg';
|
||||
render(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
mediaSrc=""
|
||||
mediaSrc={mediaSrc}
|
||||
node={authenticatedNode}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
||||
expect(screen.getByText('label.loading')).toBeInTheDocument();
|
||||
const image = screen.getByTestId('uploaded-image-node');
|
||||
|
||||
expect(image).toBeInTheDocument();
|
||||
expect(image).toHaveAttribute('src', mediaSrc);
|
||||
});
|
||||
|
||||
it('should reset states when url or mediaSrc changes', async () => {
|
||||
it('should reset states when url changes', async () => {
|
||||
const { rerender } = render(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
mediaSrc="https://example.com/image1.jpg"
|
||||
node={mockNode}
|
||||
/>
|
||||
);
|
||||
|
||||
// Simulate image load
|
||||
const image = screen.getByTestId('uploaded-image-node');
|
||||
fireEvent.load(image);
|
||||
|
||||
// Rerender with new url
|
||||
const newNode = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
url: 'https://example.com/new-image.jpg',
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
rerender(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
mediaSrc="https://example.com/image1.jpg"
|
||||
node={newNode}
|
||||
/>
|
||||
);
|
||||
|
||||
// Image should be hidden again until it loads
|
||||
expect(screen.getByTestId('uploaded-image-node')).toHaveAttribute(
|
||||
'src',
|
||||
'https://example.com/image1.jpg'
|
||||
);
|
||||
});
|
||||
|
||||
it('should reset states when mediaSrc changes', async () => {
|
||||
const { rerender } = render(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
@ -146,6 +203,31 @@ describe('ImageAttachment', () => {
|
||||
);
|
||||
|
||||
// Image should be hidden again until it loads
|
||||
expect(image).toHaveStyle({ visibility: 'hidden' });
|
||||
expect(screen.getByTestId('uploaded-image-node')).toHaveAttribute(
|
||||
'src',
|
||||
'https://example.com/image2.jpg'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty alt text', () => {
|
||||
const nodeWithEmptyAlt = {
|
||||
...mockNode,
|
||||
attrs: {
|
||||
...mockNode.attrs,
|
||||
alt: '',
|
||||
},
|
||||
} as unknown as NodeViewProps['node'];
|
||||
|
||||
render(
|
||||
<ImageAttachment
|
||||
isMediaLoading={false}
|
||||
mediaSrc="https://example.com/image.jpg"
|
||||
node={nodeWithEmptyAlt}
|
||||
/>
|
||||
);
|
||||
|
||||
const image = screen.getByTestId('uploaded-image-node');
|
||||
|
||||
expect(image).toHaveAttribute('alt', '');
|
||||
});
|
||||
});
|
||||
|
||||
@ -11,13 +11,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { NodeViewProps } from '@tiptap/react';
|
||||
import { Spin } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as IconFormatImage } from '../../../../../assets/svg/ic-format-image.svg';
|
||||
import { UPLOADED_ASSETS_URL } from '../../../../../constants/BlockEditor.constants';
|
||||
import Loader from '../../../../common/Loader/Loader';
|
||||
|
||||
const ImageAttachment = ({
|
||||
node,
|
||||
@ -32,7 +29,6 @@ const ImageAttachment = ({
|
||||
const [imageError, setImageError] = useState<boolean>(false);
|
||||
const [imageLoaded, setImageLoaded] = useState<boolean>(false);
|
||||
const needsAuthentication = url?.includes(UPLOADED_ASSETS_URL);
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Reset states when url changes
|
||||
useEffect(() => {
|
||||
@ -56,34 +52,25 @@ const ImageAttachment = ({
|
||||
|
||||
return (
|
||||
<div className="image-wrapper">
|
||||
<Spin
|
||||
indicator={<Loader size="small" />}
|
||||
spinning={showLoadingOverlay}
|
||||
tip={isUploading ? t('label.uploading') : t('label.loading')}>
|
||||
<div
|
||||
className={classNames('image-container', {
|
||||
'loading-state': showLoadingOverlay || imageError,
|
||||
})}>
|
||||
{(showLoadingOverlay || imageError) && (
|
||||
<div className="loading-overlay">
|
||||
<IconFormatImage width={40} />
|
||||
</div>
|
||||
)}
|
||||
{displaySrc && (
|
||||
<img
|
||||
alt={alt ?? ''}
|
||||
data-testid="uploaded-image-node"
|
||||
src={displaySrc}
|
||||
style={{
|
||||
visibility: imageLoaded ? 'visible' : 'hidden',
|
||||
display: 'block',
|
||||
}}
|
||||
onError={handleImageError}
|
||||
onLoad={handleImageLoad}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Spin>
|
||||
<div
|
||||
className={classNames('image-container', {
|
||||
'loading-state': showLoadingOverlay || imageError,
|
||||
})}
|
||||
data-testid="image-container">
|
||||
{displaySrc ? (
|
||||
<img
|
||||
alt={alt ?? ''}
|
||||
data-testid="uploaded-image-node"
|
||||
src={displaySrc}
|
||||
onError={handleImageError}
|
||||
onLoad={handleImageLoad}
|
||||
/>
|
||||
) : (
|
||||
<div className="loading-overlay">
|
||||
<IconFormatImage width={40} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user