feat(08-03): FavoritesWidget frontend + API client + page.tsx wiring (GREEN)
- favorites-api.ts: FavoriteLink type + fetchFavorites/createFavorite/updateFavorite/deleteFavorite all use credentials: include and API_URL/favorites - favorites-widget.tsx: list/grid view, inline add/edit/delete in edit mode, icon + letter fallback, rel=noreferrer + target=_blank, no dangerouslySetInnerHTML (T-08-07) - useEffect deps fixed to [instanceId] only — excludes t() to prevent re-fetch on each render - page.tsx: wireFavoritesWidget(FavoritesWidget) wired - Full test suite: 81/81 pass (17 test files) - Web TypeScript: clean
This commit is contained in:
@@ -132,25 +132,28 @@ describe('FavoritesWidget', () => {
|
||||
const titleInput = screen.getByPlaceholderText('favorites.addTitle');
|
||||
const urlInput = screen.getByPlaceholderText('favorites.addUrl');
|
||||
|
||||
await act(async () => {
|
||||
// Use sync fireEvent — RTL will flush state updates automatically
|
||||
act(() => {
|
||||
fireEvent.change(titleInput, { target: { value: 'New Link' } });
|
||||
fireEvent.change(urlInput, { target: { value: 'https://example.com' } });
|
||||
});
|
||||
|
||||
// Submit the form (button with text 'favorites.addButton')
|
||||
// Submit the form
|
||||
const addButton = screen.getByRole('button', { name: /favorites\.addButton/i });
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
// createFavorite must be called with the widgetId and the entered values
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
widgetId: 'fav-1',
|
||||
title: 'New Link',
|
||||
url: 'https://example.com',
|
||||
}),
|
||||
);
|
||||
// waitFor polls until the async chain (createFavorite → setFavorites) completes
|
||||
await waitFor(() => {
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
widgetId: 'fav-1',
|
||||
title: 'New Link',
|
||||
url: 'https://example.com',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// The new link title should appear in the list
|
||||
await waitFor(() => {
|
||||
@@ -175,29 +178,37 @@ describe('FavoritesWidget', () => {
|
||||
const editButtons = screen.getAllByRole('button', {
|
||||
name: /favorites\.editButton/i,
|
||||
});
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
fireEvent.click(editButtons[0]);
|
||||
});
|
||||
|
||||
// Change the title in the inline edit form
|
||||
await waitFor(() => {
|
||||
expect(screen.getByDisplayValue('GitHub')).toBeInTheDocument();
|
||||
});
|
||||
const editTitleInput = screen.getByDisplayValue('GitHub');
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
fireEvent.change(editTitleInput, { target: { value: 'GitHub Updated' } });
|
||||
});
|
||||
|
||||
// Click Save
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /favorites\.saveButton/i })).toBeInTheDocument();
|
||||
});
|
||||
const saveButton = screen.getByRole('button', {
|
||||
name: /favorites\.saveButton/i,
|
||||
});
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
fireEvent.click(saveButton);
|
||||
});
|
||||
|
||||
// updateFavorite called with the favorite id and new title
|
||||
expect(mockUpdate).toHaveBeenCalledWith(
|
||||
'fav-id-1',
|
||||
expect.objectContaining({ title: 'GitHub Updated' }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(mockUpdate).toHaveBeenCalledWith(
|
||||
'fav-id-1',
|
||||
expect.objectContaining({ title: 'GitHub Updated' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('delete: clicking delete calls deleteFavorite with id and removes the row', async () => {
|
||||
@@ -217,12 +228,14 @@ describe('FavoritesWidget', () => {
|
||||
const deleteButtons = screen.getAllByRole('button', {
|
||||
name: /favorites\.deleteButton/i,
|
||||
});
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
fireEvent.click(deleteButtons[0]);
|
||||
});
|
||||
|
||||
// deleteFavorite called with the correct id
|
||||
expect(mockDelete).toHaveBeenCalledWith('fav-id-1');
|
||||
await waitFor(() => {
|
||||
expect(mockDelete).toHaveBeenCalledWith('fav-id-1');
|
||||
});
|
||||
|
||||
// The deleted favorite is removed from the list
|
||||
await waitFor(() => {
|
||||
|
||||
Reference in New Issue
Block a user