1

Problem

I am testing some Blaze templates that include <svg> elements with various <g> children. The tests use jQuery to check, whether the correct children are rendered by various input.

While I can at least check for the existence of svg I can't however get any of it's children - neither by class nor by id.

Empty child elements

Logging the svg .children to the console reveals for every child {}. The templates are rendering fine when running my application and this behavior only occurs when trying to access the children of the Template in the tests.

Empty SVG contentDocument

I am also not able to retrieve the svg's content doc:

// does unfortunately not return contentDoc
const svgDom = template.find('svg').get(0).contentDoc;

Code Example

To reproduce, consider the following template:

<template name="svgtest">
  <svg width="100%" height="100%">
    <g class="element1"></g>
    <g class="element2"></g>
  </svg>
</template>

and the corresponding tests:

import { _ } from 'meteor/underscore';
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
import { Tracker } from 'meteor/tracker';
import { assert } from 'meteor/practicalmeteor:chai';

// the test helpers, you know them
// from the Meteor testing guide

const withDiv = function withDiv(callback) {
  const el = document.createElement('div');
  document.body.appendChild(el);
  try {
    callback(el);
  } finally {
    document.body.removeChild(el);
  }
};

const withRenderedTemplate = function withRenderedTemplate(template, data, callback) {
  withDiv((el) => {
    const ourTemplate = _.isString(template) ? Template[template] : template;
    Blaze.renderWithData(ourTemplate, data, el);
    Tracker.flush();
    callback(el);
  });
};

describe('svgtest', function () {

  beforeEach(function () {
    Template.registerHelper('_', key => key);
  });

  afterEach(function () {
    Template.deregisterHelper('_');
  });


  it('renders correctly with simple data', function () {

    withRenderedTemplate('svgtest', {}, (el) => {
      const template = $(el);

      // passes
      assert.equal(template.find('svg').length, 1);

      // does unfortunately not return contentDoc
      const svgDom = template.find('svg').get(0).contentDoc;

      // fails with AssertionError: expected 0 to equal 1
      assert.equal(template.find('.element1').length, 1);
      assert.equal(template.find('.element2').length, 1);
    });
  });
});

Related

I checked already the following SO resources to access the elements:

retrieve child elements of svg g

JQuery and SVG - how to find the 'g' tag

How to use jquery in SVG (Scalable Vector Graphics)?

How to access SVG elements with Javascript

However, they only work in my running app but not in the tests. I also would like to avoid the jQuery svg plugin as this messes up the Meteor builtin jQuery.

I assume that this needs a Blaze-specific solution in order to make the SVG DOM accessible to the unit context.

Has somebody solved this already or found a workaround?

Jankapunkt
  • 8,128
  • 4
  • 30
  • 59

1 Answers1

1

Actually I found the answer myself and I think it's worth to be shared.

So the problem is that the SVG Elements are not rendered into the root element yet.

In order to make them appear in the DOM the withRenderedTemplate method basically needs to wait some short time before firing the callback.

const withRenderedTemplate = function withRenderedTemplate(template, data, callback) {
  withDiv((el) => {
    const ourTemplate = _.isString(template) ? Template[template] : template;
    Blaze.renderWithData(ourTemplate, data, el);
    Tracker.flush();
    setTimeout(() => {
      callback(el);
    }, 100);
  });
};

At this point the elements will appear within the DOM and are accessible like any other element by id or by class.

Jankapunkt
  • 8,128
  • 4
  • 30
  • 59