Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,59 @@ test.describe(
}
});

test('User with TEST_CASE.VIEW_ALL can view library test SQL expression in UI', async ({
viewResultsPage,
}) => {
const sqlExpression = 'SELECT COUNT(*) FROM {table}';
let testDefinitionRequestUrl = '';

await viewResultsPage.route(
'**/api/v1/dataQuality/testDefinitions/*',
async (route) => {
testDefinitionRequestUrl = route.request().url();

await route.fulfill({
status: 200,
contentType: 'application/json',
json: {
id: 'test-definition-id',
name: 'tableRowCountToBeBetween',
parameterDefinition: [],
sqlExpression,
},
});
}
);

const testDefinitionResponse = viewResultsPage.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions/') &&
response.request().method() === 'GET' &&
response.ok()
);

await visitTestCaseDetailsPage(viewResultsPage);
await testDefinitionResponse;
await waitForAllLoadersToDisappear(viewResultsPage);

await expect(
viewResultsPage.getByTestId('test-case-result-tab-container')
).toBeVisible();
expect(decodeURIComponent(testDefinitionRequestUrl)).toContain(
'sqlExpression'
);
const sqlExpressionContainer = viewResultsPage.getByTestId(
'sql-expression-container'
);
await expect(sqlExpressionContainer).toBeVisible({ timeout: 15000 });
await expect(sqlExpressionContainer).toContainText('SQL Expression');
await expect(sqlExpressionContainer).toContainText(sqlExpression);

await viewResultsPage.unroute(
'**/api/v1/dataQuality/testDefinitions/*'
);
});

test('User with TABLE.VIEW_TESTS can view test case and results in UI (alternative)', async ({
tableEditResultsPage,
}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { compare } from 'fast-json-patch';
import chunk from 'lodash/chunk';
import isEmpty from 'lodash/isEmpty';
import isUndefined from 'lodash/isUndefined';
import startCase from 'lodash/startCase';
import toString from 'lodash/toString';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -102,7 +101,10 @@ const TestCaseResultTab = () => {
if (testCaseData?.testDefinition?.id) {
try {
const definition = await getTestDefinitionById(
testCaseData.testDefinition.id
testCaseData.testDefinition.id,
{
fields: ['parameterDefinition', 'sqlExpression'],
}
);
setTestDefinition(definition);
} catch (error) {
Expand Down Expand Up @@ -181,6 +183,30 @@ const TestCaseResultTab = () => {
);
}, [testCaseData?.parameterValues]);

const sqlExpressionItems = useMemo(() => {
if (!isEmpty(withSqlParams)) {
return withSqlParams.map((param) => ({
isEditable: true,
key: param.name ?? 'sqlExpression',
label: t('label.sql-expression'),
value: param.value ?? '',
}));
}

if (!isEmpty(testDefinition?.sqlExpression)) {
return [
{
isEditable: false,
key: 'test-definition-sql-expression',
label: t('label.sql-expression'),
value: testDefinition?.sqlExpression ?? '',
},
];
}

return [];
}, [testDefinition?.sqlExpression, t, withSqlParams]);

const handleTagSelection = async (selectedTags: EntityTags[]) => {
if (!testCaseData) {
return;
Expand Down Expand Up @@ -489,21 +515,21 @@ const TestCaseResultTab = () => {
</div>
</div>

{!isUndefined(withSqlParams) && !isVersionPage ? (
{!isEmpty(sqlExpressionItems) && !isVersionPage ? (
<div className="tw:w-full">
{withSqlParams.map((param) => (
{sqlExpressionItems.map((param) => (
<div
className="sql-expression-container"
data-testid="sql-expression-container"
key={param.name}>
key={param.key}>
<div className="tw:flex tw:w-full tw:flex-col tw:gap-1">
<div className="tw:flex tw:flex-row tw:items-center tw:gap-1">
<Typography
as="span"
className="parameter-title tw:text-body">
{startCase(param.name)}
{param.label}
</Typography>
{hasEditPermission && (
{hasEditPermission && param.isEditable && (
<EditIconButton
newLook
data-testid="edit-sql-param-icon"
Expand All @@ -523,7 +549,7 @@ const TestCaseResultTab = () => {
styleActiveLine: false,
readOnly: true,
}}
value={param.value ?? ''}
value={param.value}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,36 @@ jest.mock('../../../common/EntityDescription/DescriptionV1', () => {
return jest.fn().mockImplementation(() => <div>DescriptionV1</div>);
});
jest.mock('../../../Database/SchemaEditor/SchemaEditor', () => {
return jest.fn().mockImplementation(() => <div>SchemaEditor</div>);
return jest
.fn()
.mockImplementation(({ value }) => (
<div data-testid="schema-editor">SchemaEditor: {value}</div>
));
});
jest.mock('../../../Database/Profiler/TestSummary/TestSummary', () => {
return jest.fn().mockImplementation(() => <div>TestSummary</div>);
});
jest.mock('../../../../utils/EntityVersionUtils', () => ({
getComputeRowCountDiffDisplay: jest.fn(),
getEntityVersionByField: jest.fn(),
getEntityVersionTags: jest.fn().mockReturnValue([]),
getParameterValueDiffDisplay: jest.fn(),
}));
jest.mock('../../../../utils/TableUtils', () => ({
getTagsWithoutTier: jest
.fn()
.mockImplementation((tags = []) =>
tags.filter((tag: TagLabel) => !tag.tagFQN?.startsWith('Tier.'))
),
getTierTags: jest
.fn()
.mockImplementation((tags = []) =>
tags.find((tag: TagLabel) => tag.tagFQN?.startsWith('Tier.'))
),
}));
jest.mock('../../../../utils/TagsUtils', () => ({
createTagObject: jest.fn().mockImplementation((tags) => tags),
}));
jest.mock('../../AddDataQualityTest/EditTestCaseModal', () => {
return jest.fn().mockImplementation(({ onUpdate, testCase, onCancel }) => (
<div>
Expand All @@ -127,10 +152,10 @@ const mockGetTestDefinitionById = jest.fn();
jest.mock('../../../../rest/testAPI', () => ({
updateTestCaseById: jest
.fn()
.mockImplementation(() => mockUpdateTestCaseById()),
.mockImplementation((...args) => mockUpdateTestCaseById(...args)),
getTestDefinitionById: jest
.fn()
.mockImplementation(() => mockGetTestDefinitionById()),
.mockImplementation((...args) => mockGetTestDefinitionById(...args)),
TestCaseType: {
all: 'all',
table: 'table',
Expand Down Expand Up @@ -163,6 +188,8 @@ describe('TestCaseResultTab', () => {
);
mockUseTestCaseStore.testCase.useDynamicAssertion = undefined;
mockUseTestCaseStore.testCase.computePassedFailedRowCount = undefined;
mockGetTestDefinitionById.mockReset();
mockUpdateTestCaseById.mockReset();
});

it('Should render component', async () => {
Expand Down Expand Up @@ -199,6 +226,67 @@ describe('TestCaseResultTab', () => {
expect(await screen.findByText('EditTestCaseModal')).toBeInTheDocument();
});

it('Should fetch test definition with sqlExpression field', async () => {
mockGetTestDefinitionById.mockResolvedValue({
id: '48063740-ac35-4854-9ab3-b1b542c820fe',
name: 'tableColumnCountToEqual',
});

render(<TestCaseResultTab />);

await screen.findByTestId('test-case-result-tab-container');

expect(mockGetTestDefinitionById).toHaveBeenCalledWith(
'48063740-ac35-4854-9ab3-b1b542c820fe',
{
fields: ['parameterDefinition', 'sqlExpression'],
}
);
});

it('Should render SQL expression from test definition for library based test', async () => {
mockUseTestCaseStore.testCase.parameterValues = [
{
name: 'columnCount',
value: '10',
},
];
mockGetTestDefinitionById.mockResolvedValue({
id: '48063740-ac35-4854-9ab3-b1b542c820fe',
name: 'tableColumnCountToEqual',
sqlExpression: 'SELECT COUNT(*) FROM {table}',
});

render(<TestCaseResultTab />);

expect(await screen.findByText('label.sql-expression')).toBeInTheDocument();
expect(
await screen.findByText('SchemaEditor: SELECT COUNT(*) FROM {table}')
).toBeInTheDocument();
expect(screen.queryByTestId('edit-sql-param-icon')).not.toBeInTheDocument();
});

it('Should prefer test case SQL expression over test definition SQL expression', async () => {
mockUseTestCaseStore.testCase.parameterValues = [
{ name: 'sqlExpression', value: 'select * from dim_address' },
];
mockGetTestDefinitionById.mockResolvedValue({
id: '48063740-ac35-4854-9ab3-b1b542c820fe',
name: 'tableColumnCountToEqual',
sqlExpression: 'SELECT COUNT(*) FROM {table}',
});

render(<TestCaseResultTab />);

expect(
await screen.findByText('SchemaEditor: select * from dim_address')
).toBeInTheDocument();
expect(
screen.queryByText('SchemaEditor: SELECT COUNT(*) FROM {table}')
).not.toBeInTheDocument();
expect(screen.getByTestId('edit-sql-param-icon')).toBeInTheDocument();
});

it('EditTestCaseModal should be removed on cancel click', async () => {
const { container } = render(<TestCaseResultTab />);

Expand Down
Loading