@@ -5,6 +5,16 @@ import { useBuildingsStore } from "../store";
55
66let fake : FakeMap ;
77
8+ const createBasemapLod2LayerMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
9+
10+ vi . mock ( "../basemap-lod2" , async ( importOriginal ) => {
11+ const actual = await importOriginal < typeof import ( "../basemap-lod2" ) > ( ) ;
12+ return {
13+ ...actual ,
14+ createBasemapLod2Layer : createBasemapLod2LayerMock ,
15+ } ;
16+ } ) ;
17+
818vi . mock ( "@/lib/MapContext" , ( ) => ( {
919 useMap : ( ) => ( {
1020 mapRef : { current : fake . map } ,
@@ -16,6 +26,7 @@ vi.mock("@/lib/MapContext", () => ({
1626import { BuildingExtrusionLayer } from "../map-layer" ;
1727
1828const LAYER_ID = "openmapx-3d-buildings" ;
29+ const DETAILED_LAYER_ID = "openmapx-3d-buildings-lod2" ;
1930const BUILDING_LAYER_ID = "base-buildings" ;
2031const SYMBOL_LAYER_ID = "place-labels" ;
2132
@@ -46,6 +57,14 @@ function addBaseStyle(): void {
4657beforeEach ( ( ) => {
4758 fake = createFakeMap ( { zoom : 16 , pitch : 20 , maxPitch : 70 } ) ;
4859 addBaseStyle ( ) ;
60+ createBasemapLod2LayerMock . mockReset ( ) ;
61+ createBasemapLod2LayerMock . mockImplementation ( async ( id : string ) => ( {
62+ id,
63+ type : "custom" ,
64+ renderingMode : "3d" ,
65+ loadStatus : 1 ,
66+ render : vi . fn ( ) ,
67+ } ) ) ;
4968 useBuildingsStore . setState ( { panelOpen : false , layerVisible : false } ) ;
5069 vi . stubGlobal (
5170 "matchMedia" ,
@@ -71,6 +90,58 @@ describe("BuildingExtrusionLayer", () => {
7190 expect ( layer ?. source ) . not . toBe ( "unrelated" ) ;
7291 } ) ;
7392
93+ it ( "replaces the fallback with detailed LoD2 geometry at z16 in Germany" , async ( ) => {
94+ fake . state . center = { lng : 8.6821 , lat : 50.1109 } ;
95+ useBuildingsStore . setState ( { layerVisible : true } ) ;
96+
97+ render ( < BuildingExtrusionLayer /> ) ;
98+ await vi . waitFor ( ( ) => expect ( fake . state . layers . has ( DETAILED_LAYER_ID ) ) . toBe ( true ) ) ;
99+ expect ( createBasemapLod2LayerMock ) . toHaveBeenCalled ( ) ;
100+ expect ( fake . state . layout . get ( DETAILED_LAYER_ID ) ?. visibility ) . toBe ( "visible" ) ;
101+ expect ( fake . state . layout . get ( LAYER_ID ) ?. visibility ) . toBe ( "none" ) ;
102+ expect ( fake . state . layout . get ( BUILDING_LAYER_ID ) ?. visibility ) . toBe ( "none" ) ;
103+
104+ act ( ( ) => {
105+ fake . state . center = { lng : - 73.9855 , lat : 40.758 } ;
106+ fake . emit ( "moveend" ) ;
107+ } ) ;
108+ expect ( fake . state . layers . has ( DETAILED_LAYER_ID ) ) . toBe ( false ) ;
109+ expect ( fake . state . layout . get ( LAYER_ID ) ?. visibility ) . toBe ( "visible" ) ;
110+ } ) ;
111+
112+ it ( "keeps the extrusion fallback below z16 and outside Germany" , async ( ) => {
113+ fake . state . center = { lng : 8.6821 , lat : 50.1109 } ;
114+ fake . state . zoom = 15 ;
115+ useBuildingsStore . setState ( { layerVisible : true } ) ;
116+
117+ render ( < BuildingExtrusionLayer /> ) ;
118+ await act ( async ( ) => Promise . resolve ( ) ) ;
119+
120+ expect ( createBasemapLod2LayerMock ) . not . toHaveBeenCalled ( ) ;
121+ expect ( fake . state . layout . get ( LAYER_ID ) ?. visibility ) . toBe ( "visible" ) ;
122+
123+ fake . state . zoom = 16 ;
124+ fake . state . center = { lng : - 73.9855 , lat : 40.758 } ;
125+ act ( ( ) => fake . emit ( "moveend" ) ) ;
126+ await act ( async ( ) => Promise . resolve ( ) ) ;
127+
128+ expect ( createBasemapLod2LayerMock ) . not . toHaveBeenCalled ( ) ;
129+ expect ( fake . state . layout . get ( LAYER_ID ) ?. visibility ) . toBe ( "visible" ) ;
130+ } ) ;
131+
132+ it ( "falls back without hiding buildings when the detailed renderer fails" , async ( ) => {
133+ fake . state . center = { lng : 8.6821 , lat : 50.1109 } ;
134+ createBasemapLod2LayerMock . mockRejectedValueOnce ( new Error ( "renderer unavailable" ) ) ;
135+ const warn = vi . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
136+ useBuildingsStore . setState ( { layerVisible : true } ) ;
137+
138+ render ( < BuildingExtrusionLayer /> ) ;
139+
140+ await vi . waitFor ( ( ) => expect ( warn ) . toHaveBeenCalledTimes ( 1 ) ) ;
141+ expect ( fake . state . layers . has ( DETAILED_LAYER_ID ) ) . toBe ( false ) ;
142+ expect ( fake . state . layout . get ( LAYER_ID ) ?. visibility ) . toBe ( "visible" ) ;
143+ } ) ;
144+
74145 it ( "re-anchors after idle when the overlay is restored before its layer exists" , ( ) => {
75146 useBuildingsStore . setState ( { layerVisible : true } ) ;
76147 render ( < BuildingExtrusionLayer /> ) ;
0 commit comments