Fastboot rehydration: Server side HTML vs Client side HTML


#1

I’m working on some code that tests fastboot rendered applications inside of Ember’s test runner.

I’m able to render the fastboot response and use assert.dom to test the server rendered content. It’s pretty cool!

Here’s an example test…

import { module, test, skip } from 'qunit';
import { setup, visit, renderedHtml } from 'ember-cli-fastboot-testing/test-support';

module('Fastboot | basic', function(hooks) {
  setup(hooks);

  skip('it renders html that matches the browser', async function(assert) {
    let { body } = await visit('/');

    assert.equal(body, renderedHtml());
  });

  test('it renders the correct h1 title', async function(assert) {
    await visit('/');

    assert.dom('h1').includesText('FastbootTesting');
  });

  test('it renders the correct og:title', async function(assert) {
    let { htmlDocument } = await visit('/');

    assert
      .dom('meta[property="og:title"]', htmlDocument)
      .hasAttribute('content', 'Fastboot testing');
  });

  test('it gets a success response code', async function(assert) {
    let { statusCode } = await visit('/');

    assert.equal(statusCode, 200);
  });

});

and here’s a preview of it running

Rehydration

@rwjblue suggested having these tests verify that the server rendered HTML matches the browser’s client side rendering, so that we know the client side HTML can be rehydrated correctly. It’s kinda awesome that we can do this!

Here’s why this matters… in fastboot, it’s possible generate html that’s invalid. For example, you could render this on the server:

<p><div>hi</div></p>

and the browser will auto correct it to:

<p></p>
<div>hi</div>
<p></p>

It turns out div tags are not allowed inside of p! This re-arranging of HTML will screw up Ember’s rehydration of the server rendered HTML.

In order to test this, I’m rendering the fastboot’s body html and then comparing it to the brower’s rendering.

let fastbootHtml = '<body> ...';

document.querySelector('#ember-testing').innerHTML = fastbootHtml;

let browserHtml = document.querySelector('#ember-testing').innerHTML;

fastbootHtml === browserHtml; // do these match!?

Rehydration is a black box to me and I’m noticing a couple of differences between the fastboot rendered content and the browser corrected content. I’d love to understand how these affect rehydration.

  1. Does fastbootHtml and browserHtml need to be an exact string match? If not, what does glimmer need to successfully rehydrate?

  2. What do I want to compare? Fastboot ships an entire HTML document, but the testing container is made for rendering an application template. I don’t believe I can render <!DOCTYPE>, <html>, or <head> tags inside of #ember-testing? Right now I’m only rendering what’s between the fastboot body boundaries.

  3. Is innerHTML the best way to test this? Or is comparing the dom’s tree structure better?

  4. In testing an app I noticed the server will render <div data-hello></div> and the browser correct that to <div data-hello=""></div>. How do attributes affect rehydration?

  5. In testing an addon that ships an SVG I noticed that the server renders <path ... /> in fastboot, but the browser auto corrects it to <path ...></path>. How do self closing tags affect rehydration?

  6. Do different browsers auto correct HTML in different ways? Are there differences that should be ignored?

  7. What other questions should I be asking!?

Thanks for reading!


#2

Sorry didn’t have a chance to answer the questions just yet, but thank you for working on this! I really like the way the API is coming together.


#3

You will want to diff the DOM trees, not the strings. That answers several of your sub-questions, because things like self-closing tags or data-hello vs data-hello="" aren’t going to affect the DOM structure.

The HTML spec covers error recovery, so they’re supposed to do it the same. I think you should just trust that you’re dealing with a spec-compliant HTML parser and diff the resulting DOM trees.


#4

Ok that’s great to hear and like you said answers many of those questions. Also, DOM trees should be easier to compare!

I’ll whip something up this week and report back :smiley:

Btw, I had no idea that error recovery was part of the HTML spec