117 lines
5.1 KiB
JavaScript
117 lines
5.1 KiB
JavaScript
// orderFlow imports geocodeAddress from a component that pulls in
|
|
// @astryxdesign ESM, which jest does not transform. Only the function matters
|
|
// here, and every assertion below supplies its own place, so the fallback
|
|
// geocode is stubbed to "found nothing" — which is also the case the last
|
|
// test covers.
|
|
jest.mock('components/nearle_components/AddressAutocomplete', () => ({
|
|
geocodeAddress: jest.fn().mockResolvedValue(null)
|
|
}));
|
|
|
|
import { ORDER_STEPS } from '@/lib/assistant/orderFlow';
|
|
|
|
/**
|
|
* The address step of the conversational order flow.
|
|
*
|
|
* It was reading Google's raw Places shape — `address_components` and
|
|
* `geometry.location.lat()` — from a value that never has either.
|
|
* `geocodeAddress` returns whatever `getAddressSuggestions` produced, and that
|
|
* goes through `standardizePlace`, which emits a FLAT place: formatted_address,
|
|
* city, postcode, latitude, longitude.
|
|
*
|
|
* So the forEach never ran and both coordinates came back undefined. Every
|
|
* order created through the assistant was missing its delivery coordinates —
|
|
* the thing the step's own comment says the dispatch optimiser routes on — and
|
|
* the separate "what's the delivery pincode?" step fired every time because
|
|
* nothing had ever set it.
|
|
*/
|
|
|
|
const addressStep = ORDER_STEPS.find((s) => s.id === 'deliveryaddress');
|
|
|
|
// What geocodingService.standardizePlace actually returns.
|
|
const flatPlace = {
|
|
formatted_address: '18 Race Course Road, Coimbatore, Tamil Nadu 641018',
|
|
name: '18 Race Course Road',
|
|
suburb: 'Race Course',
|
|
city: 'Coimbatore',
|
|
state: 'Tamil Nadu',
|
|
postcode: '641018',
|
|
latitude: 11.0038,
|
|
longitude: 76.9645
|
|
};
|
|
|
|
describe('the address step', () => {
|
|
it('is an address step, so the UI can render a search field for it', () => {
|
|
// A type the renderer does not know falls through to the plain chat
|
|
// composer — which is how this ended up asking for a full address as free
|
|
// text with no suggestions.
|
|
expect(addressStep.type).toBe('address');
|
|
});
|
|
|
|
it('reads coordinates off the FLAT place shape', async () => {
|
|
const { value } = await addressStep.resolve('race course road', { place: flatPlace });
|
|
expect(value.deliverylatitude).toBeCloseTo(11.0038);
|
|
expect(value.deliverylongitude).toBeCloseTo(76.9645);
|
|
});
|
|
|
|
it('reads city and pincode off the flat shape too', async () => {
|
|
const { value } = await addressStep.resolve('race course road', { place: flatPlace });
|
|
expect(value.deliverycity).toBe('Coimbatore');
|
|
expect(value.deliverypincode).toBe('641018');
|
|
});
|
|
|
|
it('skips the pincode question once the place supplied one', async () => {
|
|
// `when: (d) => !d.deliverypincode` — the step only exists for addresses
|
|
// that could not supply one. It used to fire every single time.
|
|
const pincodeStep = ORDER_STEPS.find((s) => s.id === 'deliverypincode');
|
|
const { value } = await addressStep.resolve('race course road', { place: flatPlace });
|
|
const draft = addressStep.apply({}, value);
|
|
expect(pincodeStep.when(draft)).toBe(false);
|
|
});
|
|
|
|
it('still understands a raw Google Places result', async () => {
|
|
// Kept as a fallback so a caller handing in an unstandardised result does
|
|
// not silently lose its coordinates the way this step used to.
|
|
const googlePlace = {
|
|
formatted_address: '5 Gandhipuram, Coimbatore',
|
|
geometry: { location: { lat: () => 11.0168, lng: () => 76.9558 } },
|
|
address_components: [
|
|
{ long_name: 'Coimbatore', types: ['locality'] },
|
|
{ long_name: '641012', types: ['postal_code'] }
|
|
]
|
|
};
|
|
const { value } = await addressStep.resolve('gandhipuram', { place: googlePlace });
|
|
expect(value.deliverylatitude).toBeCloseTo(11.0168);
|
|
expect(value.deliverycity).toBe('Coimbatore');
|
|
expect(value.deliverypincode).toBe('641012');
|
|
});
|
|
|
|
it('accepts plain lat/lng numbers, not only getter functions', async () => {
|
|
const { value } = await addressStep.resolve('somewhere', {
|
|
place: { formatted_address: 'X', geometry: { location: { lat: 12.5, lng: 77.5 } } }
|
|
});
|
|
expect(value.deliverylatitude).toBe(12.5);
|
|
expect(value.deliverylongitude).toBe(77.5);
|
|
});
|
|
|
|
it('never emits a coordinate pair with only one half', async () => {
|
|
// A booking with a latitude and no longitude is worse than one with
|
|
// neither: it looks located and routes to the wrong place.
|
|
const { value } = await addressStep.resolve('nowhere', {
|
|
place: { formatted_address: 'No coords', city: 'Coimbatore' }
|
|
});
|
|
expect(value.deliverylatitude).toBeUndefined();
|
|
expect(value.deliverylongitude).toBeUndefined();
|
|
// and it still keeps what it DID learn
|
|
expect(value.deliverycity).toBe('Coimbatore');
|
|
expect(value.deliveryaddress).toBe('No coords');
|
|
});
|
|
|
|
it('refuses rather than inventing when nothing resolves', async () => {
|
|
const result = await addressStep.resolve('qqqq', { place: null });
|
|
// With no option place it falls back to geocoding, which in a test
|
|
// environment resolves to nothing — the step must report that, not guess.
|
|
expect(result.error || result.value).toBeTruthy();
|
|
if (result.error) expect(result.error).toMatch(/couldn.t find that address/i);
|
|
});
|
|
});
|