<p>jsdom estragou tudo com a função getComputedTextLength ou getBBox</p>

Criado em 24 nov. 2016  ·  16Comentários  ·  Fonte: jsdom/jsdom

Estou enfrentando um problema ao tentar obter o tamanho do elemento de texto usando d3:

Eu tentei getComputedTextLength ou getBBox, minha biblioteca indo bem, mas quando o teste simulado com jsdom retornou o seguinte erro:

TypeError: text.node(...).getBBox não é uma função

Alguma ideia ?

Comentários muito úteis

Para uma implementação rápida para testes no Jest:

beforeEach(() => {
  window.SVGElement.prototype.getBBox = () => ({
    x: 0,
    y: 0, 
    // whatever other props you need
  });
});

afterEach(() => {
  delete window.SVGElement.prototype.getBBox;
});

Provavelmente uma solução melhor com mocks, mas precisa ser pragmático, então apenas faça o que funciona por enquanto.

Todos 16 comentários

jsdom não faz nenhum layout, então nenhum desses métodos retornaria nada sensato. Você pode apenas deslocá-los em seu retorno de chamada created e retornar valores simulados.

@Sebmaster obrigado
Espero que o jsdom dê suporte ao layout em breve,

Mesmo problema aqui. Como você pode "shim" os métodos no retorno de chamada, você poderia dar um exemplo?

Obrigado, eu tentei

 const dom = new JSDOM('<!DOCTYPE html><body><p>Hello world</p></body></html>', {
   beforeParse(window) {
     HTMLUnknownElement.getComputedTextLength = (() => 200);
   }
 });

mas ainda recebo o erro TypeError: tspan.node(...).getComputedTextLength is not a function . Qual é o elemento certo para simular? 😕

Não, você provavelmente quer window.HTMLElement. Mas eu não sei o que tspan.node(...) retorna, então talvez registre o que é isso.

Você também precisa anexá-lo ao protótipo, não como um método estático.

Para referência futura; a função de nó retorna um window.Element. Isto é o que funcionou para nós:

const dom = new JSDOM('<!DOCTYPE html><body><p>Hello world</p></body></html>', {
  beforeParse(window) {
    window.Element.prototype.getComputedTextLength = function() {
      return 200
    }
  }
})

Aqui está uma função simples que você pode implementar para polyfill para getBBox

getBoundingBox(svg) {
    if (svg.getBBox) {
        return svg.getBBox()
    } else if (svg.attributes.d) {
        let xmin, xmax, ymin, ymax
        let path = svg.attributes.d.value

        path = path.replace(/[a-z].*/g," ").replace(/[\sA-Z]+/gi," ").trim()

        const coordinate_list = path.split(" ")

        for (var coordinate of coordinate_list) {
            if (coordinate.length > 1) {
                let initial_coordinate = coordinate.split(",")
                xmin = xmax = initial_coordinate[0]
                ymin = ymax = initial_coordinate[1]
                break
            }
        }

        for (var coordinate of coordinate_list) {
            let xycoord = coordinate.split(",")
            if (!xycoord[1]) {
                // ignore relative movements
                xycoord[0] = xmin
                xycoord[1] = ymin
            }
            xmin = Math.min(xmin, xycoord[0])
            xmax = Math.max(xmax, xycoord[0])
            ymin = Math.min(ymin, xycoord[1])
            ymax = Math.max(ymax, xycoord[1])
        }
        return [[xmin, ymax], [xmax, ymin]]
    }
}

@jordancoil você usa esta função para estender SVGGraphicsElement?

@rssfrncs Ele apenas permite que você obtenha as coordenadas da caixa delimitadora em navegadores que não têm suporte para getBBox()

@jordancoil Polyfill interessante, você sabe se isso também leva em consideração os estilos aplicados ao elemento svg?
Parece que ele verifica apenas as coordenadas do caminho, portanto, se a largura do traçado do caminho for 20 px ou 1px, retornaremos a mesma bbox. Legal de qualquer maneira ter um polyfill simplificado assim

Para uma implementação rápida para testes no Jest:

beforeEach(() => {
  window.SVGElement.prototype.getBBox = () => ({
    x: 0,
    y: 0, 
    // whatever other props you need
  });
});

afterEach(() => {
  delete window.SVGElement.prototype.getBBox;
});

Provavelmente uma solução melhor com mocks, mas precisa ser pragmático, então apenas faça o que funciona por enquanto.

beforeEach(() => {
    window.SVGElement.prototype.getComputedTextLength = () => 200;
  });

Graças a @larrybotha , isso funcionou para mim para getComputedTextLength.

@larrybotha @ronvenna ,

quando estou implementando a solução acima, estou recebendo o seguinte erro:

TypeError: Não é possível ler a propriedade 'protótipo' de indefinido

gentilmente me guie

@megha-ui não tenho certeza porque SVGElement não estaria disponível em window mas você _pode_ ser capaz de adicionar SVGElement a window :

beforeEach(() => {
  // naive way to add SVGElement to window if it doesn't exist
  if (!window.SVGElement) {
    window.SVGElement = {};
  }

  window.SVGElement.prototype.getBBox = () => ({
    x: 0,
    y: 0, 
    // whatever other props you need
  });
});

Isso não foi testado e provavelmente mascarará apenas um problema subjacente - se você receber outros erros depois de fazer isso, essa não é a abordagem correta e você precisará determinar qual é o problema subjacente.

Você pode começar inspecionando o objeto window dentro beforeEach para determinar se SVGElement está lá ou não (veja as dicas de solução de problemas do Jest ), embora neste ponto o StackOverflow seja um lugar melhor para obter feedback.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

josephrexme picture josephrexme  ·  4Comentários

domenic picture domenic  ·  3Comentários

potapovDim picture potapovDim  ·  4Comentários

mitar picture mitar  ·  4Comentários

philipwalton picture philipwalton  ·  4Comentários