簡體   English   中英

在 jsdom 中加載的 Google Map 缺少 map 磁貼和控件

[英]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:

谷歌地圖不會掛載 map(及其控件),直到它確保容器在視覺上可見並且大小 > 0。

(您可以通過在.html文件中編寫 html 並為容器設置height: 0樣式來驗證它。然后你會看到它不會創建 Z1D78DC8ED51214E518B51104FE24490AEZ00 00 px 直到你inspector器設置height: 400px 40000000 px 。

jsdom pretendToBeVisual: true是如何工作的:

jsdom pretendToBeVisual: true並沒有真正渲染任何東西。 所以getBoundingClientRectclientHeightclientWidth and... 將返回0

Jsdon/Google-maps 渲染問題:

當您同時使用 googl googl-mapsjsdom時: google-maps將加載,但它不會創建實際的 map,因為它認為容器大小為 0 或不可見。

解決方案:

我試圖覆蓋與size相關的 DomElement 方法和屬性,例如: offsetHeightclientHeight以及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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM