embermap / ember-cli-fastboot-testing

Test your FastBoot-rendered HTML alongside your application's tests.
https://embermap.github.io/ember-cli-fastboot-testing
MIT License
39 stars 18 forks source link

Allow user to pass functions inside the `options` parameter while calling `visit` #160

Closed haochuan closed 4 years ago

haochuan commented 4 years ago

Feature Request

This is the follow up feature request for https://github.com/embermap/ember-cli-fastboot-testing/issues/60.

https://github.com/embermap/ember-cli-fastboot-testing/issues/60 give us the ability to pass custom visit options to fastboot.visit. Since it's using JSON.stringify to prepare the body for calling /_fastboot-testing, any function will be dropped during this process.

Example Use Case

This is very critical to our Ember Fastboot apps since we are using a number of heavily customized addons in our use case. In order to use this addon to test our SSR content, we need to have the way to pass functions in the options to fastboot.visit.

Sample test file:

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

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

  test('custom visit options', async function(assert) {
    const options = {
      metadata: {
        myFunc: () => {}
      }
    }
    await visit('/', options);
    // ....
  });
});

In this case, the actual option in fastboot.visit will be:

{
  metadata: {}
}

Proposal

Although json is not designed to store any function in general, it's good to give users the ability to pass anything to fastboot. In the proposed changes below, we are using a simple helper library json-fn, to stringify and parse the options.

In addon-test-support/index.js:

import JSONfn from 'json-fn';
// ...
let fetchFromEmberCli = async function(url, options) {
  // ...
  try {
    response = await fetch('/__fastboot-testing', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        url,
        options: JSONfn.stringify(options), // before: options: options
      }),
    });
  } catch (e) {
      // ...
};

In index.js:

let JSONfn = require('json-fn');

module.exports = {
  // ...
  _fastbootRenderingMiddleware(app) {
    // ...
    app.post('/__fastboot-testing', bodyParser.json(), (req, res) => {
      let urlToVisit = decodeURIComponent(req.body.url);
      let parsed = url.parse(urlToVisit, true);

      // ...

      // before:  let options = Object.assign(defaultOptions, req.body.options);
      let options = Object.assign(defaultOptions, JSONfn.parse(req.body.options));
      // ...
    });
  },
};

Please let me know if that makes sense, I will have a PR for this soon 😄 . Thanks!

ryanto commented 4 years ago

Released as 0.2.2!