import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, waitFor, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { server } from '@availity/mock/src/lib/server';
import avMessages from '@availity/message-core';
import { FavoritesProvider } from './Favorites';
import { FavoriteHeart } from './FavoriteHeart';
vi.mock('@availity/message-core');
const domain = () => {
if (window.location.origin) {
return window.location.origin;
}
if (window.location.hostname) {
return `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''
}`;
}
return '*';
};
avMessages.subscribe = vi.fn((event, fn) => {
window.addEventListener('message', (event) => {
if (!event || !event.data) {
// check origin as trusted domain
return;
}
let { data } = event;
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch {
// do nothing
}
}
event = data && data.event;
fn(data);
});
return () => vi.fn();
});
avMessages.send = vi.fn((payload, target) => {
try {
const message = typeof payload === 'string' ? payload : JSON.stringify(payload);
target?.postMessage(message, domain());
} catch (error) {
// eslint-disable-next-line no-console
console.warn('AvMessage.send()', error);
}
});
const queryClient = new QueryClient();
describe('Favorites', () => {
beforeAll(() => {
// Start the interception.
server.listen();
});
afterEach(() => {
// Remove any handlers you may have added
// in individual tests (runtime handlers).
server.resetHandlers();
vi.clearAllMocks();
queryClient.clear();
});
// terminate the server
afterAll(() => server.close());
it('should render favorited', async () => {
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-123');
await waitFor(() => expect(heart).toBeChecked());
});
it('should render unfavorited', async () => {
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-789');
await waitFor(() => expect(heart).not.toBeChecked());
});
it('should render disabled', async () => {
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-789');
await waitFor(() => expect(heart).toBeDisabled());
});
it('should show tooltip with add message', async () => {
const { container, getByRole } = render(
);
const heart = container.querySelector('#av-favorite-heart-789');
act(() => {
if (heart) fireEvent.mouseOver(heart);
});
expect(heart).toBeDefined();
await waitFor(
() => {
const tooltip = getByRole('tooltip');
expect(tooltip.textContent).toContain('Add to My Favorites');
},
{ timeout: 2000 }
);
});
it('should show tooltip with remove message', async () => {
const { container, getByRole } = render(
);
const heart = container.querySelector('#av-favorite-heart-123');
act(() => {
if (heart) fireEvent.mouseOver(heart);
});
expect(heart).toBeDefined();
await waitFor(
() => {
const tooltip = getByRole('tooltip');
expect(tooltip.textContent).toContain('Remove from My Favorites');
},
{ timeout: 2000 }
);
});
it('should render label with app name', async () => {
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-123');
expect(heart).toBeDefined();
expect(heart).toHaveAttribute('aria-label', 'Favorite App #1');
});
it('should add favorite and send post message with updated favorites', async () => {
const user = userEvent.setup();
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-789');
await waitFor(() => expect(heart).not.toBeChecked());
if (heart) await user.click(heart);
await waitFor(() => expect(heart).toBeChecked());
await waitFor(() => {
expect(avMessages.send).toHaveBeenCalledTimes(1);
expect(avMessages.send).toHaveBeenCalledWith({
event: 'av:favorites:update',
favorites: [
{ id: '123', pos: 0 },
{ id: '456', pos: 1 },
{ id: '789', pos: 2 },
],
});
});
});
it('should toggle favorite and send post message with updated favorites on keypress', async () => {
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-123');
await waitFor(() => {
expect(heart).toBeChecked();
});
if (heart) await fireEvent.keyDown(heart, { key: 'Enter', code: 'Enter', charCode: 13 });
await waitFor(() => expect(heart).not.toBeChecked());
await waitFor(() => {
expect(avMessages.send).toHaveBeenCalledTimes(1);
expect(avMessages.send).toHaveBeenCalledWith({
event: 'av:favorites:update',
favorites: [{ id: '456', pos: 1 }],
});
});
});
it('should remove favorite and send post message with updated favorites', async () => {
const user = userEvent.setup();
const { container } = render(
);
const heart = container.querySelector('#av-favorite-heart-123');
await waitFor(() => expect(heart).toBeChecked());
if (heart) await user.click(heart);
await waitFor(() => expect(heart).not.toBeChecked());
await waitFor(() => {
expect(avMessages.send).toHaveBeenCalledTimes(1);
expect(avMessages.send).toHaveBeenCalledWith({
event: 'av:favorites:update',
favorites: [{ id: '456', pos: 1 }],
});
});
});
});