fix(provider-website): default contact.paymentMethods to [] (site-wide Footer crash)
The edge-served provider-config (black_api down) returns a populated contact object WITHOUT paymentMethods. validateProviderData only substituted a default contact when the whole object was falsy, so a present-but-incomplete contact passed through with paymentMethods undefined. The Footer (rendered site-wide) and ContactCard both call contact.paymentMethods.map() unguarded → TypeError 'Cannot read properties of undefined (reading map)' → every page crashed. Add validateContact() to normalize paymentMethods to an array while preserving all other contact fields (mirrors validateAboutSection). Regression test added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4503f86573
commit
29592405d4
2 changed files with 55 additions and 6 deletions
|
|
@ -0,0 +1,36 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { validateProviderData } from './providerDataValidator';
|
||||
|
||||
describe('validateProviderData — contact normalization', () => {
|
||||
it('defaults paymentMethods to [] when a present contact omits it', () => {
|
||||
// Regression: the live edge-served provider-config (black_api down) returns a
|
||||
// populated contact WITHOUT paymentMethods. The Footer (site-wide) and
|
||||
// ContactCard call contact.paymentMethods.map() unguarded, so an absent
|
||||
// array crashed every page. The validator must force it to an array even
|
||||
// when the contact object itself is present/truthy.
|
||||
const data = {
|
||||
contact: { phone: '+1', email: 'q@example.com', instagram: 'quinn' },
|
||||
};
|
||||
const result = validateProviderData(data);
|
||||
expect(Array.isArray(result.contact.paymentMethods)).toBe(true);
|
||||
expect(result.contact.paymentMethods).toHaveLength(0);
|
||||
// Existing contact fields are preserved.
|
||||
expect(result.contact.phone).toBe('+1');
|
||||
// The crash site is now safe.
|
||||
expect(() => result.contact.paymentMethods.map((pm) => pm)).not.toThrow();
|
||||
});
|
||||
|
||||
it('preserves a valid paymentMethods array', () => {
|
||||
const data = {
|
||||
contact: { phone: '+1', paymentMethods: [{ method: 'Cash' }, { method: 'Crypto' }] },
|
||||
};
|
||||
const result = validateProviderData(data);
|
||||
expect(result.contact.paymentMethods).toHaveLength(2);
|
||||
expect(result.contact.paymentMethods[0].method).toBe('Cash');
|
||||
});
|
||||
|
||||
it('defaults the whole contact (incl. paymentMethods) when contact is absent', () => {
|
||||
const result = validateProviderData({});
|
||||
expect(Array.isArray(result.contact.paymentMethods)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -45,6 +45,24 @@ function ensureActivityMenus(value: unknown): ActivityMenu[] {
|
|||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the contact block. The API may return a contact object that is
|
||||
* present (phone, email, socials) but missing the `paymentMethods` array — the
|
||||
* Footer and ContactCard both call `contact.paymentMethods.map(...)` unguarded,
|
||||
* so an absent array crashes every page (the Footer renders site-wide). Force
|
||||
* `paymentMethods` to an array while preserving all other contact fields.
|
||||
*/
|
||||
function validateContact(contact: unknown): ProviderData['contact'] {
|
||||
if (!contact || typeof contact !== 'object') {
|
||||
return { phone: '', communicationNote: '', responseTime: '', paymentMethods: [] };
|
||||
}
|
||||
const obj = contact as Row;
|
||||
return {
|
||||
...(obj as ProviderData['contact']),
|
||||
paymentMethods: ensureArray(obj.paymentMethods),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and normalize ProviderData from the API.
|
||||
* Ensures all arrays are present and non-null, preventing downstream crashes.
|
||||
|
|
@ -85,12 +103,7 @@ export function validateProviderData(data: unknown): ProviderData {
|
|||
tour: ensureArray(obj.tour),
|
||||
currentLocation: obj.currentLocation as ProviderData['currentLocation'] || null,
|
||||
gallery: ensureArray(obj.gallery),
|
||||
contact: obj.contact as ProviderData['contact'] || {
|
||||
phone: '',
|
||||
communicationNote: '',
|
||||
responseTime: '',
|
||||
paymentMethods: [],
|
||||
},
|
||||
contact: validateContact(obj.contact),
|
||||
etiquette: ensureArray(obj.etiquette),
|
||||
policies: ensureArray(obj.policies),
|
||||
about: validateAboutSection(obj.about),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue