157 lines
6.5 KiB
JavaScript
157 lines
6.5 KiB
JavaScript
import React from 'react';
|
|
import { configure, render, screen, fireEvent, within } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
|
|
configure({ asyncUtilTimeout: 5000 });
|
|
|
|
/**
|
|
* Rider (miler) create and edit forms. The hooks are mocked; the server rules
|
|
* (duplicate phone, hub in another city, block holding) are covered by
|
|
* doormile_backend's routes_miler_account_pg_test.go.
|
|
*/
|
|
|
|
beforeAll(() => {
|
|
global.ResizeObserver = global.ResizeObserver || class { observe() {} unobserve() {} disconnect() {} };
|
|
// Radix Select needs these in jsdom.
|
|
window.HTMLElement.prototype.hasPointerCapture = window.HTMLElement.prototype.hasPointerCapture || (() => false);
|
|
window.HTMLElement.prototype.releasePointerCapture = window.HTMLElement.prototype.releasePointerCapture || (() => {});
|
|
window.HTMLElement.prototype.scrollIntoView = window.HTMLElement.prototype.scrollIntoView || (() => {});
|
|
});
|
|
|
|
jest.mock('lucide-react', () =>
|
|
new Proxy({}, { get: (_t, prop) => (prop === '__esModule' ? true : (props) => <span data-testid={`icon-${String(prop)}`} {...props} />) })
|
|
);
|
|
|
|
const mockCreate = jest.fn();
|
|
const mockUpdate = jest.fn();
|
|
const HUBS = [
|
|
{ hubid: 11, hubname: 'Coimbatore Neptune Hub', applocationid: 1 },
|
|
{ hubid: 21, hubname: 'Hyderabad Mars Hub', applocationid: 2 },
|
|
];
|
|
const CITIES = [
|
|
{ applocationid: 1, locationname: 'Coimbatore' },
|
|
{ applocationid: 2, locationname: 'Hyderabad' },
|
|
];
|
|
let mockRider = null;
|
|
|
|
jest.mock('@/lib/doormileHooks', () => ({
|
|
useTenants: () => ({ data: [{ tenantid: 5, tenantname: "Sai's Kitchen" }] }),
|
|
useHubs: () => ({ data: HUBS }),
|
|
useAppLocations: () => ({ data: CITIES }),
|
|
useCreateMiler: () => ({ mutate: mockCreate, isPending: false }),
|
|
useUpdateMiler: () => ({ mutate: mockUpdate, isPending: false }),
|
|
useMiler: () => ({ data: mockRider, isLoading: false }),
|
|
}));
|
|
|
|
import CreateRider from '@/pages/doormile/riders/CreateRider';
|
|
import EditRider from '@/pages/doormile/riders/EditRider';
|
|
|
|
/** Opens a Radix select by its trigger and returns the visible option labels. */
|
|
const openSelect = (trigger) => {
|
|
fireEvent.keyDown(trigger, { key: 'Enter' });
|
|
return screen.getAllByRole('option').map((o) => o.textContent);
|
|
};
|
|
const pick = (trigger, label) => {
|
|
openSelect(trigger);
|
|
fireEvent.click(screen.getByRole('option', { name: label }));
|
|
};
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
localStorage.clear();
|
|
mockRider = null;
|
|
});
|
|
|
|
describe('Create miler', () => {
|
|
const fill = () => {
|
|
fireEvent.change(screen.getByPlaceholderText('9876543210'), { target: { value: '9876500001' } });
|
|
fireEvent.change(screen.getByPlaceholderText('murali@example.com'), { target: { value: 'ravi@r.test' } });
|
|
const inputs = screen.getAllByRole('textbox');
|
|
// Display name and login name are the first two text inputs.
|
|
fireEvent.change(inputs[0], { target: { value: 'Ravi K' } });
|
|
fireEvent.change(inputs[1], { target: { value: 'ravi' } });
|
|
};
|
|
|
|
it('asks for no password: the rider sets their own MPIN in the app', () => {
|
|
render(<MemoryRouter><CreateRider /></MemoryRouter>);
|
|
expect(screen.queryByText('Initial password')).not.toBeInTheDocument();
|
|
expect(screen.getByText(/sets their own MPIN/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('offers only the hubs of the chosen city', () => {
|
|
render(<MemoryRouter><CreateRider /></MemoryRouter>);
|
|
// Staff form: vehicle, client, city, hub.
|
|
const [, , city, hub] = screen.getAllByRole('combobox');
|
|
pick(city, 'Coimbatore');
|
|
const options = openSelect(hub);
|
|
expect(options).toContain('Coimbatore Neptune Hub');
|
|
expect(options).not.toContain('Hyderabad Mars Hub');
|
|
});
|
|
|
|
it("creates a rider in Doormile's own pool when no client is chosen", () => {
|
|
render(<MemoryRouter><CreateRider /></MemoryRouter>);
|
|
fill();
|
|
pick(screen.getAllByRole('combobox')[2], 'Coimbatore');
|
|
fireEvent.click(screen.getByRole('button', { name: /create miler/i }));
|
|
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
const payload = mockCreate.mock.calls[0][0];
|
|
expect(payload).toMatchObject({ tenantid: 0, applocationid: 1, contactno: '9876500001' });
|
|
expect(payload).not.toHaveProperty('password');
|
|
});
|
|
});
|
|
|
|
describe('Edit miler', () => {
|
|
const RIDER = {
|
|
milerprofileid: 7, displayname: 'Nagalakshmi', phone: '9876500004', email: 'naga@r.test',
|
|
tenantid: 5, applocationid: 1, hubid: 11, defaultvehicletype: 'Scooter',
|
|
};
|
|
const renderEdit = () =>
|
|
render(
|
|
<MemoryRouter initialEntries={[{ pathname: '/doormile/milers/edit', state: { rider: RIDER } }]}>
|
|
<EditRider />
|
|
</MemoryRouter>
|
|
);
|
|
|
|
it('saves the changed phone (and not the unchanged hub) — and shows email, client and city read-only', () => {
|
|
mockRider = RIDER;
|
|
renderEdit();
|
|
expect(screen.getByText('naga@r.test')).toBeInTheDocument();
|
|
expect(screen.getByText("Sai's Kitchen")).toBeInTheDocument();
|
|
expect(screen.getAllByText(/Can't be changed here/)).toHaveLength(3);
|
|
|
|
fireEvent.change(screen.getByDisplayValue('9876500004'), { target: { value: '9876500007' } });
|
|
fireEvent.click(screen.getByRole('button', { name: /save changes/i }));
|
|
expect(mockUpdate).toHaveBeenCalledTimes(1);
|
|
const { id, data } = mockUpdate.mock.calls[0][0];
|
|
expect(id).toBe(7);
|
|
// Only what changed: re-sending the old hub could fail a legacy rider's save.
|
|
expect(data).toEqual({ displayname: 'Nagalakshmi', contactno: '9876500007', defaultvehicletype: 'Scooter' });
|
|
});
|
|
|
|
it('a rename alone does not resend the login phone', () => {
|
|
mockRider = RIDER;
|
|
renderEdit();
|
|
fireEvent.change(screen.getByDisplayValue('Nagalakshmi'), { target: { value: 'Nagalakshmi R' } });
|
|
fireEvent.click(screen.getByRole('button', { name: /save changes/i }));
|
|
expect(mockUpdate.mock.calls[0][0].data).toEqual({ displayname: 'Nagalakshmi R', defaultvehicletype: 'Scooter' });
|
|
});
|
|
|
|
it("offers only hubs in the rider's city", () => {
|
|
mockRider = RIDER;
|
|
renderEdit();
|
|
const hubTrigger = screen.getAllByRole('combobox')[1];
|
|
const options = openSelect(hubTrigger);
|
|
expect(options).toContain('Coimbatore Neptune Hub');
|
|
expect(options).not.toContain('Hyderabad Mars Hub');
|
|
});
|
|
|
|
it('refuses a mobile number that is not 10 digits', () => {
|
|
mockRider = RIDER;
|
|
renderEdit();
|
|
fireEvent.change(screen.getByDisplayValue('9876500004'), { target: { value: '98765' } });
|
|
fireEvent.click(screen.getByRole('button', { name: /save changes/i }));
|
|
expect(mockUpdate).not.toHaveBeenCalled();
|
|
expect(within(document.body).getByText('Enter a 10-digit mobile number')).toBeInTheDocument();
|
|
});
|
|
});
|