import { TextEncoder, TextDecoder } from 'util'; import CheckoutErrorNotice, { normaliseSection } from '../shared/components/checkout-error-notice'; import type { CheckoutApiError, CheckoutIssue } from '../types'; let renderToStaticMarkup: ( element: JSX.Element ) => string; beforeAll( async () => { // react-dom/server needs these, which the jsdom test environment lacks. Object.assign( globalThis, { TextEncoder, TextDecoder } ); ( { renderToStaticMarkup } = await import( 'react-dom/server' ) ); Object.assign( window, { parcel2goShipping: { editOrderUrlTemplate: 'post.php?post=__ID__&action=edit' }, } ); } ); // The shape the validate endpoint returns for a too-short tariff code. const tariffIssue = ( itemRef: string ): CheckoutIssue => ( { type: 'errorcodes/checkout/invalid-requirement', title: 'Field Less Than Minimum Length', detail: 'Field tariffCode was less than minimum length of 8', instance: 'request[1]', itemId: 1, itemRef, field: 'tariffCode', section: 'parcelContent', } ); const render = ( issues: CheckoutIssue[], inlineFields?: string[] ) => renderToStaticMarkup( ); // What the API returns for an empty required identifier. const missingCode = ( field: string, itemRef = '29' ): CheckoutIssue => ( { type: 'errorcodes/checkout/invalid-requirement', title: 'Required Field Missing', detail: `Field ${ field } was missing a required value`, itemRef, field, section: 'parcelContent', } ); describe( 'normaliseSection', () => { it( 'treats parcelContent as item details, whatever the casing', () => { expect( normaliseSection( 'parcelContent' ) ).toBe( 'parcel' ); expect( normaliseSection( 'parcelcontent' ) ).toBe( 'parcel' ); } ); it( 'leaves other sections alone', () => { expect( normaliseSection( 'origin' ) ).toBe( 'origin' ); expect( normaliseSection( 'billingAddress' ) ).toBe( 'billingAddress' ); } ); it( 'returns null for an absent section', () => { expect( normaliseSection( null ) ).toBeNull(); expect( normaliseSection( undefined ) ).toBeNull(); } ); } ); describe( 'CheckoutErrorNotice parcelContent issues', () => { it( 'names the order and links to it', () => { const html = render( [ tariffIssue( '29' ) ] ); expect( html ).toContain( 'Tariff Code' ); expect( html ).toContain( 'item details on the order' ); expect( html ).toContain( 'Fix on order #29' ); expect( html ).toContain( 'post.php?post=29&action=edit' ); expect( html ).not.toContain( 'related order' ); } ); it( 'lists every affected order when several fail the same way', () => { const html = render( [ tariffIssue( '29' ), tariffIssue( '30' ) ] ); expect( html ).toContain( 'Affects orders:' ); expect( html ).toContain( '#29' ); expect( html ).toContain( '#30' ); } ); } ); describe( 'CheckoutErrorNotice issues fixed in the form beside it', () => { it( 'points an inline field at the form, with no order link', () => { const html = render( [ missingCode( 'manufacturerProductId' ) ], [ 'manufacturerProductId' ] ); expect( html ).toContain( 'Required Field Missing' ); expect( html ).toContain( 'Manufacturer ID' ); expect( html ).toContain( 'product codes above' ); expect( html ).not.toContain( 'Fix on order' ); expect( html ).not.toContain( 'post.php' ); } ); it( 'matches the field name whatever the case', () => { const html = render( [ missingCode( 'merchantProductId' ) ], [ 'MERCHANTPRODUCTID' ] ); expect( html ).toContain( 'product codes above' ); expect( html ).not.toContain( 'Fix on order' ); } ); it( 'still links the order for a field that is not in the form', () => { const html = render( [ missingCode( 'manufacturerProductId' ) ], [ 'merchantProductId' ] ); expect( html ).toContain( 'Fix on order #29' ); expect( html ).not.toContain( 'product codes above' ); } ); it( 'still links the order when no field is fixed inline', () => { const html = render( [ missingCode( 'manufacturerProductId' ) ] ); expect( html ).toContain( 'Fix on order #29' ); } ); it( 'keeps the order link for other issues beside an inline one', () => { const html = render( [ missingCode( 'manufacturerProductId' ), tariffIssue( '29' ) ], [ 'manufacturerProductId' ] ); expect( html ).toContain( 'product codes above' ); expect( html ).toContain( 'Fix on order #29' ); } ); it( 'does not list orders for an inline issue spanning several orders', () => { const html = render( [ missingCode( 'manufacturerProductId', '29' ), missingCode( 'manufacturerProductId', '30' ) ], [ 'manufacturerProductId' ] ); expect( html ).not.toContain( 'Affects orders:' ); } ); } );