[英]Google Map loaded in jsdom is missing map tiles and controls
我正在嘗試讓 Google Map 加載到jsdom中,以便我可以使用react-testing-library測試地圖/鼠標事件。 不幸的是, <img>
map 瓦片和<button>
map 控件沒有加載到 dom 中。 我沒有從 Google Maps 或 jsdom 收到任何錯誤消息,所以我不確定問題出在哪里。
我正在使用以下軟件包:
canvas@2.6.1
jest@26.1.0
jsdom@16.2.2
@testing-library/jest-dom@5.11.0
@testing-library/react@10.4.4
這是一個最小示例,基於 Google 的同步 map 加載示例(已編輯 API 密鑰):
import { JSDOM } from 'jsdom';
import '@testing-library/jest-dom/extend-expect';
import { render, waitFor } from '@testing-library/react';
const dom = new JSDOM(`
<!DOCTYPE html>
<html>
<head>
<title>Synchronous Loading</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
<script>
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
</script>
</body>
</html>
`, {
pretendToBeVisual: true,
resources: 'usable',
runScripts: 'dangerously',
});
global.window = dom.window;
global.document = dom.window.document;
describe('Google Map', () => {
it('has a zoom button', async () => {
const { queryByRole } = render(); // just checking the dom
await waitFor(() => expect(queryByRole('button', { name: 'Zoom in' })).toBeInTheDocument(), { timeout: 5000 });
});
});
此測試因超時而失敗。 調試器顯示 map div 具有以下內容,其中缺少很多應有的內容(地圖圖塊、map 控件等):
<div id="map" style="position: relative; overflow: hidden;">
<div style="height: 100%; width: 100%; position: absolute; top: 0px; left: 0px; background-color: rgb(229, 227, 223);">
<div style="overflow: hidden;"/>
<div class="gm-style" style="position: absolute; z-index: 0; left: 0px; top: 0px; height: 100%; width: 100%; padding: 0px; border-width: 0px; margin: 0px;">
<div style="position: absolute; z-index: 0; left: 0px; top: 0px; height: 100%; width: 100%; padding: 0px; border-width: 0px; margin: 0px;cursor: url(https://maps.gstatic.com/mapfiles/openhand_8_8.cur), default;" tabindex="0">
<div style="z-index: 1; position: absolute; left: 50%; top: 50%; width: 100%; transform: translate(0px,0px);">
<div style="position: absolute; left: 0px; top: 0px; z-index: 100; width: 100%;">
<div style="position: absolute; left: 0px; top: 0px; z-index: 0;">
<div style="position: absolute; z-index: 992; transform: matrix(1,0,0,1,-32,-20);">
<div style="position: absolute; left: 0px; top: 0px; width: 256px; height: 256px;">
<div style="width: 256px; height: 256px;" />
</div>
</div>
</div>
</div>
<div style="position: absolute; left: 0px; top: 0px; z-index: 101; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 102; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 103; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 0;" />
</div>
<div class="gm-style-pbc" style="z-index: 2; position: absolute; height: 100%; width: 100%; padding: 0px; border-width: 0px; margin: 0px left: 0px; top: 0px; transition-duration: 0; opacity: 0;">
<p class="gm-style-pbt" />
</div>
<div style="z-index: 3; position: absolute; height: 100%; width: 100%; padding: 0px; border-width: 0px; margin: 0px; left: 0px; top: 0px;">
<div style="z-index: 4; position: absolute; left: 50%; top: 50%; width: 100%; transform: translate(0px,0px);">
<div style="position: absolute; left: 0px; top: 0px; z-index: 104; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 105; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 106; width: 100%;" />
<div style="position: absolute; left: 0px; top: 0px; z-index: 107; width: 100%;" />
</div>
</div>
</div>
<iframe aria-hidden="true" frameborder="0" style="z-index: -1; position: absolute; width: 100%; height: 100%; top: 0px; left: 0px;" tabindex="-1" />
</div>
</div>
</div>
當它到達<iframe>
時可能會遇到錯誤(因為 map 控件應該在那之后出現),但我還沒有找到進一步調試問題的方法。 有沒有辦法讓這個 map 完全加載到 jsdom 中?
谷歌地圖不會掛載 map(及其控件),直到它確保容器在視覺上可見並且大小 > 0。
(您可以通過在.html
文件中編寫 html 並為容器設置height: 0
樣式來驗證它。然后你會看到它不會創建 Z1D78DC8ED51214E518B51104FE24490AEZ00 00 px 直到你inspector
器設置height: 400px
40000000 px 。
pretendToBeVisual: true
是如何工作的: jsdom pretendToBeVisual: true
並沒有真正渲染任何東西。 所以getBoundingClientRect
、 clientHeight
和clientWidth
and... 將返回0
。
當您同時使用 googl googl-maps
和jsdom
時: google-maps
將加載,但它不會創建實際的 map,因為它認為容器大小為 0 或不可見。
我試圖覆蓋與size
相關的 DomElement 方法和屬性,例如: offsetHeight
和clientHeight
以及getBoundingClientRect
(適用於所有元素),但是由於有很多與 size 相關的屬性/方法,並且覆蓋所有這些屬性/方法很耗時,這不是一個明智的選擇。 (請注意,要覆蓋像offsetHeight
這樣的只讀屬性,您不能使用正常的覆蓋方法。相反,您應該使用以下命令覆蓋它: Object.defineProperty
。)
所以我建議嘗試尋找具有渲染支持的jsdom
替代方案(或使用在真實瀏覽器上執行的測試庫)/或嘗試閱讀google-maps
源代碼並了解resize
事件處理程序如何檢查容器是否可見與否,並覆蓋所有這些方法。
(另外,如果您不關心是否是最新的,您也可以嘗試檢查是否可以找到始終呈現 map 的舊版本的 google-maps。)
感謝@yaya的回答,我能夠讓 map 通過 mocking 安裝與大小相關的屬性和方法。 通過添加以下代碼,按鈕測試通過:
const mockWidth = 200;
const mockHeight = 200;
function shouldMockRender(element) {
const map = dom.window.document.getElementById('map');
const isMap = element.id === 'map';
const isChild = map && map.contains(element);
return isMap || isChild;
}
Object.defineProperty(dom.window.HTMLElement.prototype, 'clientWidth', {
get() {
return shouldMockRender(this) ? mockWidth : 0;
},
});
Object.defineProperty(dom.window.HTMLElement.prototype, 'clientHeight', {
get() {
return shouldMockRender(this) ? mockHeight : 0;
},
});
dom.window.HTMLElement.prototype.getBoundingClientRect = function getBoundingClientRect() {
return {
x: 0,
y: 0,
width: this.clientWidth,
height: this.clientHeight,
top: 0,
right: this.clientWidth,
bottom: this.clientHeight,
left: 0,
};
};
順便說一句,即使在測試完成后,該過程也會繼續運行。 關閉window
修復了:
describe('Google Map', () => {
afterAll(() => window.close());
it('has a zoom button', async () => {
const { queryByRole } = render(); // just checking the dom
await waitFor(() => expect(queryByRole('button', { name: 'Zoom in' })).toBeInTheDocument(), { timeout: 5000 });
});
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.