I have the following react function component:
const AssetsPage = () => {
const [filteredAssets, setFilteredAssets] = useState<Asset[]>([]);
const assetsState = useAppSelector(selectAssets);
...
useEffect(() => {
setFilteredAssets(
assetsState.assets
);
}, [assetsState.assets])
useEffect(() => {
store.dispatch(fetchAssetsAsync(null));
}, [])
const columns: GridColDef[] = [
...some column definitions...
];
return (
<>
<Typography variant="h2" ml={2}>
{t("Asset.Title")}
</Typography>
<DataTable
rows={filteredAssets}
columns={columns}
rowId="globalId"
/>
</>
)
}
export default AssetsPage
And the corresponding slice:
import {Asset} from "../../models/Asset";
import {createAsyncThunkWithErrorHandling} from "../../utils/thunkHelper";
import {createSlice} from "@reduxjs/toolkit";
import {RootState} from "../../app/store";
import {createAsset, deleteAsset, getAssets, updateAsset} from "../../api/assets";
const initialState = {
isLoaded: false,
assets: [] as Asset[],
}
...
export const fetchAssetsAsync = createAsyncThunkWithErrorHandling(
"assets/fetch",
async (payload: string) => {
return await getAssets(payload);
}
)
...
export const oeeAssetsSlice = createSlice({
name: "assets",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchAssetsAsync.fulfilled, (state, action) => {
state.isLoaded = true;
state.assets = [
...action.payload,
];
});
...
}
})
export const selectAssets = (state: RootState) => state.assets;
export default oeeAssetsSlice.reducer;
This works just fine, it loads data from the backend (or, if I replace the fetchAssetsAsync
implementation with static data, it loads it properly.)
I am trying to do some unit testing. Rendering the empty table works fine: but I cannot inject data to the assetsState
no matter how I try.
import {fireEvent, render, screen, waitFor} from "@testing-library/react";
import {Provider} from "react-redux";
import {RootState, store} from "../../../app/store";
import AssetsPage from "./AssetsPage";
import {createTheme} from "@mui/material/styles";
import {ThemeOptions} from "../../ThemeOptions/ThemeOptions";
import {ThemeProvider} from "@mui/material";
import oeeAssetsReducer, {selectAssets} from "../../../features/assets/oeeAssetsSlice";
import {useAppSelector} from "../../../app/hooks";
import appReducer from "../../../features/app/appSlice";
import userReducer from "../../../features/user/userSlice";
describe("AssetListPage", () => {
const theme = createTheme(ThemeOptions);
// This test works fine.
test("renders the empty component", async () => {
render(
<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.getByText("No rows")).toBeInTheDocument();
});
// todo: this doesnt work.
const asset = {
globalId: "asset-1",
description: "test asset",
businessUnitCode: "bu-1",
localId: "123",
enabledFlag: 1,
};
jest.mock("../../../app/hooks", () => ({
useAppSelector: (selectAssets: any) => ({
assetsState: {
isLoaded: true,
assets: [asset]
}
})
}));
jest.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key
})
}));
test("renders the component with one data line", async () => {
render(<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
render(
<ThemeProvider theme={theme}>
<Provider store={store}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.getByText("Asset.GlobalAssetId")).toBeInTheDocument();
expect(screen.getByText("Asset.Description")).toBeInTheDocument();
expect(screen.getByText("Asset.BusinessUnit")).toBeInTheDocument();
expect(screen.getByText("Asset.LocalId")).toBeInTheDocument(); // it renders the "empty table" instead
expect(screen.getByText("Asset.Disable")).toBeInTheDocument();
expect(screen.getByText("Asset.Actions")).toBeInTheDocument();
})
});
The test always renders a table without any data. What am I missing here? I cannot install Enzyme, tried feeding this to chatGPT, but nothing worked so far.
Here is a possible solution:
test ("render the emtpy component with one data line", async () => {
const mockUser = getAWhoamiSlice_SUPERADMIN()
const mockShifts = getATestShiftsReducer_EMTPY()
const mockBusinessUnits =getBusinessUnitsReducer_EMPTY()
const mockAssets = getAssetsReducer_TWO_ITEMS()
const mockStore = configureStore({
middleware: undefined,
reducer: {
whoami: whoamiReducer,
shifts: oeeShiftsReducer,
businessUnits: oeeBusinessUnitReducer,
assets: oeeAssetsReducer,
},
preloadedState: {
whoami: mockUser,
shifts: mockShifts,
businessUnits: mockBusinessUnits,
assets: mockAssets
},
});
render(
<ThemeProvider theme={theme}>
<Provider store={mockStore}>
<AssetsPage/>
</Provider>
</ThemeProvider>
);
expect(screen.queryByText("No rows")).not.toBeInTheDocument();
expect(screen.getByText("Asset.GlobalAssetId")).toBeInTheDocument();
expect(screen.getByText("Asset.Description")).toBeInTheDocument();
expect(screen.queryByText(mockAssets.assets[0].name)).toBeInTheDocument();
})
Some explanation for the unseen functions: