mirror of
https://github.com/apache/superset.git
synced 2026-06-29 03:15:34 +00:00
Compare commits
2 Commits
chore/ci/s
...
remove-fav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2355c7162 | ||
|
|
320cc6831c |
@@ -0,0 +1,259 @@
|
|||||||
|
/**
|
||||||
|
* 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 fetchMock from 'fetch-mock';
|
||||||
|
import { screen, waitFor } from 'spec/helpers/testing-library';
|
||||||
|
import {
|
||||||
|
API_ENDPOINTS,
|
||||||
|
renderChartList,
|
||||||
|
setupMocks,
|
||||||
|
} from './ChartList.testHelpers';
|
||||||
|
|
||||||
|
jest.setTimeout(30000);
|
||||||
|
|
||||||
|
const mockUser = {
|
||||||
|
userId: 1,
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'User',
|
||||||
|
roles: {
|
||||||
|
Admin: [
|
||||||
|
['can_read', 'Chart'],
|
||||||
|
['can_write', 'Chart'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ChartList - Favorite Column Visibility', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.resetHistory();
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when no charts are favorited', async () => {
|
||||||
|
// Mock favorite status API to return all false
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: false },
|
||||||
|
{ id: 1, value: false },
|
||||||
|
{ id: 2, value: false },
|
||||||
|
{ id: 3, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderChartList(mockUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Test Chart 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be hidden - check that favorite stars are not present
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
|
||||||
|
// Verify that other columns are still present (check table headers)
|
||||||
|
expect(
|
||||||
|
screen.getByRole('columnheader', { name: 'Name' }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('columnheader', { name: 'Type' }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows favorite column when at least one chart is favorited', async () => {
|
||||||
|
// Mock favorite status API to return mixed favorites
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true }, // This chart is favorited
|
||||||
|
{ id: 1, value: false },
|
||||||
|
{ id: 2, value: false },
|
||||||
|
{ id: 3, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderChartList(mockUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Test Chart 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be visible - wait for stars to appear
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
const favoriteStars = screen.getAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars.length).toBeGreaterThan(0);
|
||||||
|
},
|
||||||
|
{ timeout: 10000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows favorite column when all charts are favorited', async () => {
|
||||||
|
// Mock favorite status API to return all true
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true },
|
||||||
|
{ id: 1, value: true },
|
||||||
|
{ id: 2, value: true },
|
||||||
|
{ id: 3, value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderChartList(mockUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Test Chart 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be visible
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
const favoriteStars = screen.getAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars.length).toBeGreaterThan(0);
|
||||||
|
},
|
||||||
|
{ timeout: 10000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when user is not logged in', async () => {
|
||||||
|
// Mock favorite status API
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true },
|
||||||
|
{ id: 1, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Render without userId (user not logged in)
|
||||||
|
const noUser = {
|
||||||
|
userId: null,
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
roles: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
renderChartList(noUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Test Chart 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be hidden when user is not logged in
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when chart list is empty', async () => {
|
||||||
|
// Mock empty charts response
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHARTS,
|
||||||
|
{
|
||||||
|
result: [],
|
||||||
|
chart_count: 0,
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock empty favorite status
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderChartList(mockUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// No favorite stars should be present when there are no charts
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles partial favorite status loading gracefully', async () => {
|
||||||
|
// Mock partial favorite status (fewer items than charts)
|
||||||
|
fetchMock.get(
|
||||||
|
API_ENDPOINTS.CHART_FAVORITE_STATUS,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: false },
|
||||||
|
{ id: 1, value: false },
|
||||||
|
// Missing status for charts 2 and 3
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderChartList(mockUser);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('chart-list-view')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Test Chart 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should hide column when favorite status is incomplete
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -315,6 +315,14 @@ function ChartList(props: ChartListProps) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasFavoritesOnPage = useMemo(
|
||||||
|
() =>
|
||||||
|
charts.length > 0 &&
|
||||||
|
Object.keys(favoriteStatus).length === charts.length &&
|
||||||
|
Object.values(favoriteStatus).some(status => status === true),
|
||||||
|
[charts.length, favoriteStatus],
|
||||||
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -334,7 +342,7 @@ function ChartList(props: ChartListProps) {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
hidden: !userId,
|
hidden: !userId || !hasFavoritesOnPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Cell: ({
|
Cell: ({
|
||||||
|
|||||||
@@ -0,0 +1,316 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MemoryRouter } from 'react-router-dom';
|
||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import { isFeatureEnabled } from '@superset-ui/core';
|
||||||
|
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||||
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
|
|
||||||
|
import DashboardList from 'src/pages/DashboardList';
|
||||||
|
|
||||||
|
jest.mock('@superset-ui/core', () => ({
|
||||||
|
...jest.requireActual('@superset-ui/core'),
|
||||||
|
isFeatureEnabled: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.setTimeout(30000);
|
||||||
|
|
||||||
|
const dashboardsInfoEndpoint = 'glob:*/api/v1/dashboard/_info*';
|
||||||
|
const dashboardOwnersEndpoint = 'glob:*/api/v1/dashboard/related/owners*';
|
||||||
|
const dashboardCreatedByEndpoint =
|
||||||
|
'glob:*/api/v1/dashboard/related/created_by*';
|
||||||
|
const dashboardFavoriteStatusEndpoint =
|
||||||
|
'glob:*/api/v1/dashboard/favorite_status*';
|
||||||
|
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
|
||||||
|
|
||||||
|
const mockDashboards = [...new Array(3)].map((_, i) => ({
|
||||||
|
id: i,
|
||||||
|
url: `url-${i}`,
|
||||||
|
dashboard_title: `Dashboard ${i}`,
|
||||||
|
changed_by_name: 'user',
|
||||||
|
changed_by_fk: 1,
|
||||||
|
published: true,
|
||||||
|
changed_on_utc: new Date().toISOString(),
|
||||||
|
changed_on_delta_humanized: '5 minutes ago',
|
||||||
|
owners: [{ id: 1, first_name: 'admin', last_name: 'admin_user' }],
|
||||||
|
roles: [{ id: 1, name: 'adminUser' }],
|
||||||
|
thumbnail_url: '/thumbnail',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockUser = {
|
||||||
|
userId: 1,
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'User',
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupBasicMocks = () => {
|
||||||
|
fetchMock.reset();
|
||||||
|
|
||||||
|
fetchMock.get(dashboardsInfoEndpoint, {
|
||||||
|
permissions: ['can_read', 'can_write'],
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchMock.get(dashboardOwnersEndpoint, {
|
||||||
|
result: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchMock.get(dashboardCreatedByEndpoint, {
|
||||||
|
result: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchMock.get(dashboardsEndpoint, {
|
||||||
|
result: mockDashboards,
|
||||||
|
dashboard_count: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
global.URL.createObjectURL = jest.fn();
|
||||||
|
fetchMock.get('/thumbnail', { body: new Blob(), sendAsJson: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('DashboardList - Favorite Column Visibility', () => {
|
||||||
|
const renderDashboardList = (props = {}, userProp = mockUser) =>
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<QueryParamProvider>
|
||||||
|
<DashboardList {...props} user={userProp} />
|
||||||
|
</QueryParamProvider>
|
||||||
|
</MemoryRouter>,
|
||||||
|
{ useRedux: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setupBasicMocks();
|
||||||
|
(
|
||||||
|
isFeatureEnabled as jest.MockedFunction<typeof isFeatureEnabled>
|
||||||
|
).mockImplementation(feature => feature === 'LISTVIEWS_DEFAULT_CARD_VIEW');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.resetHistory();
|
||||||
|
fetchMock.restore();
|
||||||
|
(
|
||||||
|
isFeatureEnabled as jest.MockedFunction<typeof isFeatureEnabled>
|
||||||
|
).mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when no dashboards are favorited', async () => {
|
||||||
|
// Mock favorite status API to return all false
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: false },
|
||||||
|
{ id: 1, value: false },
|
||||||
|
{ id: 2, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDashboardList();
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboard 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be hidden - check that favorite stars are not present
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
|
||||||
|
// Verify that other columns are still present
|
||||||
|
expect(screen.getByText('Title')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows favorite column when at least one dashboard is favorited', async () => {
|
||||||
|
// Mock favorite status API to return mixed favorites
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true }, // This dashboard is favorited
|
||||||
|
{ id: 1, value: false },
|
||||||
|
{ id: 2, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDashboardList();
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboard 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be visible - check that favorite stars are present
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
const favoriteStars = screen.getAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars.length).toBeGreaterThan(0);
|
||||||
|
},
|
||||||
|
{ timeout: 10000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows favorite column when all dashboards are favorited', async () => {
|
||||||
|
// Mock favorite status API to return all true
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true },
|
||||||
|
{ id: 1, value: true },
|
||||||
|
{ id: 2, value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDashboardList();
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboard 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be visible
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
const favoriteStars = screen.getAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars.length).toBeGreaterThan(0);
|
||||||
|
},
|
||||||
|
{ timeout: 10000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when user is not logged in', async () => {
|
||||||
|
// Mock favorite status API
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: true },
|
||||||
|
{ id: 1, value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Render without userId (user not logged in)
|
||||||
|
const noUser = {
|
||||||
|
userId: 0, // Use 0 instead of null to satisfy type requirements
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
renderDashboardList({}, noUser);
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboard 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Favorite column should be hidden when user is not logged in
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hides favorite column when dashboard list is empty', async () => {
|
||||||
|
// Mock empty dashboards response
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardsEndpoint,
|
||||||
|
{
|
||||||
|
result: [],
|
||||||
|
dashboard_count: 0,
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock empty favorite status
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDashboardList();
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// No favorite stars should be present when there are no dashboards
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles partial favorite status loading gracefully', async () => {
|
||||||
|
// Mock partial favorite status (fewer items than dashboards)
|
||||||
|
fetchMock.get(
|
||||||
|
dashboardFavoriteStatusEndpoint,
|
||||||
|
{
|
||||||
|
result: [
|
||||||
|
{ id: 0, value: false },
|
||||||
|
// Missing status for dashboards 1 and 2
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ overwriteRoutes: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDashboardList();
|
||||||
|
|
||||||
|
// Wait for component to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboards')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for data to load
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Dashboard 0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should hide column when favorite status is incomplete
|
||||||
|
const favoriteStars = screen.queryAllByTestId('fave-unfave-icon');
|
||||||
|
expect(favoriteStars).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -300,6 +300,14 @@ function DashboardList(props: DashboardListProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasFavoritesOnPage = useMemo(
|
||||||
|
() =>
|
||||||
|
dashboards.length > 0 &&
|
||||||
|
Object.keys(favoriteStatus).length === dashboards.length &&
|
||||||
|
Object.values(favoriteStatus).some(status => status === true),
|
||||||
|
[dashboards.length, favoriteStatus],
|
||||||
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -319,7 +327,7 @@ function DashboardList(props: DashboardListProps) {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
hidden: !user?.userId,
|
hidden: !user?.userId || !hasFavoritesOnPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Cell: ({
|
Cell: ({
|
||||||
|
|||||||
Reference in New Issue
Block a user