@@ -373,6 +373,145 @@ describe('Location Maps Client JS', () => {
373373 } )
374374 } )
375375
376+ describe ( 'OS grid reference component' , ( ) => {
377+ beforeEach ( ( ) => {
378+ document . body . innerHTML = `
379+ <form method="post" novalidate="">
380+ <div class="govuk-form-group app-location-field " data-locationtype="osgridreffield">
381+ <div class="app-location-field-inputs">
382+ <div class="govuk-form-group">
383+ <h1 class="govuk-label-wrapper">
384+ <label class="govuk-label govuk-label--l" for="VtopAx">
385+ OS grid reference
386+ </label>
387+ </h1>
388+ <div id="VtopAx-hint" class="govuk-hint">
389+ An OS grid reference number is made up of 2 letters followed by either 6, 8 or 10 numbers, for example, TQ123456
390+ </div>
391+ <input class="govuk-input" id="VtopAx" name="VtopAx" type="text" aria-describedby="VtopAx-hint">
392+ </div>
393+ </div>
394+ </div>
395+ <div class="govuk-button-group">
396+ <button type="submit" data-prevent-double-click="true" class="govuk-button" data-module="govuk-button" data-govuk-button-init="">Continue</button>
397+ </div>
398+ </form>
399+ `
400+ } )
401+
402+ describe ( 'Map initialisation' , ( ) => {
403+ test ( 'initMaps os grid reference initializes without errors when DOM elements are present' , ( ) => {
404+ expect ( ( ) => initMaps ( ) ) . not . toThrow ( )
405+ expect ( onMock ) . toHaveBeenLastCalledWith (
406+ 'map:ready' ,
407+ expect . any ( Function )
408+ )
409+
410+ const onMapReady = onMock . mock . calls [ 0 ] [ 1 ]
411+ expect ( typeof onMapReady ) . toBe ( 'function' )
412+
413+ // Manually invoke onMapReady callback
414+ const flyToMock = jest . fn ( )
415+ onMapReady ( {
416+ map : {
417+ flyTo : flyToMock
418+ }
419+ } )
420+
421+ expect ( addPanelMock ) . toHaveBeenCalledWith ( 'info' , expect . any ( Object ) )
422+
423+ expect ( onMock ) . toHaveBeenLastCalledWith (
424+ 'interact:markerchange' ,
425+ expect . any ( Function )
426+ )
427+
428+ const input = document . body . querySelector ( 'input.govuk-input' )
429+ expect ( input ) . toBeDefined ( )
430+
431+ const osGridRefInput = /** @type {HTMLInputElement } */ ( input )
432+
433+ osGridRefInput . value = 'SJ 61831 71507'
434+ osGridRefInput . dispatchEvent ( new window . Event ( 'change' ) )
435+
436+ // Expect it to update once
437+ expect ( addMarkerMock ) . toHaveBeenCalledTimes ( 1 )
438+ expect ( flyToMock ) . toHaveBeenCalledTimes ( 1 )
439+
440+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
441+ const onInteractMarkerChange = onMock . mock . calls [ 1 ] [ 1 ]
442+ expect ( typeof onInteractMarkerChange ) . toBe ( 'function' )
443+ onInteractMarkerChange ( {
444+ coords : [ - 2.147823 , 54.155676 ]
445+ } )
446+ } )
447+
448+ test ( 'initMaps with initial values' , ( ) => {
449+ const input = document . body . querySelector ( 'input.govuk-input' )
450+ expect ( input ) . toBeDefined ( )
451+
452+ const osGridRefInput = /** @type {HTMLInputElement } */ ( input )
453+
454+ // Set some initial values prior to initMaps
455+ osGridRefInput . value = 'SJ 61831 7150'
456+
457+ expect ( ( ) => initMaps ( ) ) . not . toThrow ( )
458+ expect ( onMock ) . toHaveBeenLastCalledWith (
459+ 'map:ready' ,
460+ expect . any ( Function )
461+ )
462+
463+ const onMapReady = onMock . mock . calls [ 0 ] [ 1 ]
464+ expect ( typeof onMapReady ) . toBe ( 'function' )
465+
466+ // Manually invoke onMapReady callback
467+ const flyToMock = jest . fn ( )
468+ onMapReady ( {
469+ map : {
470+ flyTo : flyToMock
471+ }
472+ } )
473+
474+ expect ( addPanelMock ) . toHaveBeenCalledWith ( 'info' , expect . any ( Object ) )
475+
476+ expect ( onMock ) . toHaveBeenLastCalledWith (
477+ 'interact:markerchange' ,
478+ expect . any ( Function )
479+ )
480+
481+ osGridRefInput . value = 'SJ 61836 71440'
482+ osGridRefInput . dispatchEvent ( new window . Event ( 'change' ) )
483+
484+ // Expect it to update once as the field is valid
485+ expect ( addMarkerMock ) . toHaveBeenCalledTimes ( 1 )
486+ expect ( flyToMock ) . toHaveBeenCalledTimes ( 1 )
487+ } )
488+
489+ test ( 'initMaps only applies when there are location components on the page' , ( ) => {
490+ const locations = document . querySelectorAll ( '.app-location-field' )
491+
492+ // Remove any locations for the test
493+ locations . forEach ( ( location ) => {
494+ location . remove ( )
495+ } )
496+
497+ expect ( ( ) => initMaps ( ) ) . not . toThrow ( )
498+ expect ( onMock ) . not . toHaveBeenCalled ( )
499+ } )
500+
501+ test ( 'initMaps only applies when there are supported location components on the page' , ( ) => {
502+ const locations = document . querySelectorAll ( '.app-location-field' )
503+
504+ // Reset the location type of each component
505+ locations . forEach ( ( location ) => {
506+ location . setAttribute ( 'data-locationtype' , 'unknowntype' )
507+ } )
508+
509+ expect ( ( ) => initMaps ( ) ) . not . toThrow ( )
510+ expect ( onMock ) . not . toHaveBeenCalled ( )
511+ } )
512+ } )
513+ } )
514+
376515 describe ( 'Form submit event propagation' , ( ) => {
377516 beforeEach ( ( ) => {
378517 document . body . innerHTML = `
0 commit comments