126 lines
5.5 KiB
JavaScript
126 lines
5.5 KiB
JavaScript
import React from 'react';
|
|
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
|
|
/**
|
|
* "Save for later" on Create Order, then Saved Contacts in another tab.
|
|
*
|
|
* The contact didn't show up, for two reasons this pins:
|
|
* 1. The picker read only /admin/customers (customer-app accounts), while
|
|
* "save for later" writes to /admin/tenantcustomers — so a contact saved
|
|
* on the server never appeared; only the saving browser's local copy did.
|
|
* 2. The local copy was read once, on mount, so a Create Order tab that was
|
|
* already open never saw a contact saved in another tab.
|
|
*/
|
|
|
|
jest.mock('lucide-react', () =>
|
|
new Proxy({}, {
|
|
get: (_t, prop) => (prop === '__esModule' ? true : (props) => <span data-testid={`icon-${String(prop)}`} {...props} />),
|
|
})
|
|
);
|
|
jest.mock('leaflet', () => ({ __esModule: true, default: { DivIcon: function DivIcon() {} } }));
|
|
jest.mock('react-leaflet', () => ({
|
|
MapContainer: () => null, Marker: () => null, Polyline: () => null, TileLayer: () => null, useMap: () => ({}),
|
|
}));
|
|
jest.mock('@/components/doormile/AddressAutocomplete', () => ({ __esModule: true, default: () => null }));
|
|
jest.mock('@/components/doormile/MapPinPickerModal', () => ({ __esModule: true, default: () => null }));
|
|
jest.mock('@/components/doormile/HubLocationSelect', () => ({ HubLocationSelect: () => null }));
|
|
jest.mock('@/lib/doormileHooks', () => ({ useHubs: () => ({ data: [] }) }));
|
|
jest.mock('@/lib/geocodingService', () => ({ geocodeAddress: jest.fn(), saveRecentAddress: jest.fn() }));
|
|
jest.mock('@/lib/distance', () => ({
|
|
calculateDrivingDistance: jest.fn(() => Promise.resolve(null)),
|
|
calculateDrivingRoute: jest.fn(() => Promise.resolve(null)),
|
|
calculateTotalCharge: jest.fn(() => 0),
|
|
}));
|
|
jest.mock('@/components/third-party/OpenToast', () => ({ OpenToast: jest.fn() }));
|
|
jest.mock('@/api/doormile/endpoints', () => ({
|
|
createExpressBooking: jest.fn(),
|
|
createTenantCustomer: jest.fn(),
|
|
getAdminCustomers: jest.fn(),
|
|
getAdminPricing: jest.fn(() => Promise.resolve([])),
|
|
getAdminTenants: jest.fn(() => Promise.resolve([])),
|
|
getTenantCustomers: jest.fn(),
|
|
getTenantLocations: jest.fn(() => Promise.resolve([])),
|
|
}));
|
|
|
|
import CreateOrder from '@/pages/doormile/orders/CreateOrder';
|
|
import { getAdminCustomers, getTenantCustomers } from '@/api/doormile/endpoints';
|
|
|
|
const renderPage = () => {
|
|
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
return render(
|
|
<QueryClientProvider client={qc}>
|
|
<MemoryRouter>
|
|
<CreateOrder />
|
|
</MemoryRouter>
|
|
</QueryClientProvider>
|
|
);
|
|
};
|
|
|
|
const openSavedContacts = async () => {
|
|
fireEvent.click(screen.getAllByText('Saved Contacts')[0].closest('button'));
|
|
const heading = await screen.findByRole('heading', { name: 'Saved Contacts' });
|
|
return heading.closest('div.bg-white');
|
|
};
|
|
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
getAdminCustomers.mockResolvedValue([
|
|
{ appcustomerid: 7, name: 'App Customer', phone: '9111111111' },
|
|
]);
|
|
getTenantCustomers.mockResolvedValue([]);
|
|
});
|
|
|
|
describe('Create Order — Saved Contacts', () => {
|
|
it('shows a contact saved on the server, which this browser has never seen', async () => {
|
|
// Nothing in localStorage: this is the "other browser / incognito" case.
|
|
getTenantCustomers.mockResolvedValue([
|
|
{ tenantcustomerid: 42, firstname: 'Test', lastname: 'Save Contact', phone: '9000000001', address: '12 MG Road' },
|
|
]);
|
|
|
|
renderPage();
|
|
const modal = await openSavedContacts();
|
|
|
|
await waitFor(() => expect(within(modal).getByText('Test Save Contact')).toBeInTheDocument());
|
|
expect(within(modal).getByText('(9000000001)')).toBeInTheDocument();
|
|
// The customer-app accounts are still listed alongside.
|
|
expect(within(modal).getByText('App Customer')).toBeInTheDocument();
|
|
});
|
|
|
|
it('picks up a contact saved in another tab without reloading the page', async () => {
|
|
renderPage();
|
|
let modal = await openSavedContacts();
|
|
await waitFor(() => expect(within(modal).getByText('App Customer')).toBeInTheDocument());
|
|
expect(within(modal).queryByText('Other Tab Contact')).not.toBeInTheDocument();
|
|
|
|
// Another tab saves a contact: it writes localStorage, and the browser
|
|
// fires `storage` in every OTHER tab.
|
|
act(() => {
|
|
localStorage.setItem(
|
|
'doormile_saved_contacts',
|
|
JSON.stringify([{ id: 'saved_9000000002', name: 'Other Tab Contact', phone: '9000000002', isSavedForLater: true }])
|
|
);
|
|
window.dispatchEvent(new StorageEvent('storage', { key: 'doormile_saved_contacts' }));
|
|
});
|
|
|
|
modal = screen.getByRole('heading', { name: 'Saved Contacts' }).closest('div.bg-white');
|
|
await waitFor(() => expect(within(modal).getByText('Other Tab Contact')).toBeInTheDocument());
|
|
});
|
|
|
|
it('lists a number once when it is both saved locally and on the server', async () => {
|
|
localStorage.setItem(
|
|
'doormile_saved_contacts',
|
|
JSON.stringify([{ id: 'saved_9000000001', name: 'Test Save Contact', phone: '9000000001', isSavedForLater: true }])
|
|
);
|
|
getTenantCustomers.mockResolvedValue([
|
|
{ tenantcustomerid: 42, firstname: 'Test', lastname: 'Save Contact', phone: '9000000001' },
|
|
]);
|
|
|
|
renderPage();
|
|
const modal = await openSavedContacts();
|
|
await waitFor(() => expect(within(modal).getByText('App Customer')).toBeInTheDocument());
|
|
expect(within(modal).getAllByText('(9000000001)')).toHaveLength(1);
|
|
});
|
|
});
|