Skip to content

Instantly share code, notes, and snippets.

@sn3p
Last active August 14, 2019 14:26
Show Gist options
  • Save sn3p/c73992bf527177db51733819c2244619 to your computer and use it in GitHub Desktop.
Save sn3p/c73992bf527177db51733819c2244619 to your computer and use it in GitHub Desktop.
Test Elixir/Phoenix templates in JavaScript

Test EEx template in JavaScript

This example renders an Elixir/Phoenix EEx template in a JavaScript tests. I'm using cell-js and ex_css_modules to scope JS/CSS.

Rendering the template works like a charm. But when using CSS Modules the scoped classnames mapping (style.css.json file) needs to be present, otherwise rendering the template errors.

mocha test/**/*_test.js --recursive --require test/test_setup.js --timeout 10000
import { Cell, Builder } from "@defacto/cell-js";
import CSS from "./style.css";
class ExampleCell extends Cell {
initialize() {
this.hello = this.element.querySelector(`.${CSS.hello}`);
}
}
Builder.register(ExampleCell, "ExampleCell");
export default ExampleCell;
import ExampleCell from "@lib/my_web/cells/example";
import CSS from "@lib/my_web/cells/example/style.css";
import { expect } from "chai";
import { JSDOM } from "jsdom";
import { renderPhoenixView, renderPhoenixViewAsync } from "@test/test_helpers";
describe("ExampleCell", () => {
it("render phoenix template in sync", done => {
// Rendering templates in sync
// Note we're calling done() at end of test
const html = renderPhoenixView("MyWeb.ExampleCell", { hello: "world" });
const cell = renderCell(html);
cell.initialize();
expect(cell.hello).to.not.be.null;
done(); // Needed for async tests
});
describe("render phoenix template async", () => {
let cell;
before(() => {
// Rendering templates async
// No need to call done() when returning a Promise in the before hook
return renderPhoenixViewAsync("MyWeb.ExampleCell", {
hello: "world"
}).then(html => {
cell = renderCell(html);
});
});
it("exists", () => {
cell.initialize();
expect(cell.hello).to.not.be.null;
});
});
function renderCell(html) {
document.body.innerHTML = html;
return new ReadmoreCell(document.body.firstElementChild);
}
});
.hello {
color: lime;
}
{"hello":"example_3wrSd__hello"}
<%= container() do %>
<div <%= class "hello" %>>
<%= @hello %>
</div>
<% end %>
import { exec, execSync } from "child_process";
/*
* Returns the CLI command for rendering a Phoenix View.
*/
function renderPhoenixViewCommand(view, opts, template) {
const assigns = `Jason.decode!(~s(${JSON.stringify(opts)}), keys: :atoms!)`;
const html = `Phoenix.View.render_to_string(${view}, "${template}", ${assigns})`;
return `mix run -e 'IO.puts(${html})'`;
}
/*
* Renders a Phoenix View and returns the HTML.
* @example const html = renderPhoenixView("MyWeb.ExampleView", { foo: "bar" })
*/
function renderPhoenixView(view, opts = {}, template = "template.html") {
const command = renderPhoenixViewCommand(view, opts, template);
return execSync(command).toString();
}
/*
* Renders a Phoenix View and returns a Promise.
* @example renderPhoenixViewAsync("MyWeb.ExampleView").then(html => console.log(html))
*/
function renderPhoenixViewAsync(view, opts = {}, template = "template.html") {
const command = renderPhoenixViewCommand(view, opts, template);
return new Promise(resolve => {
exec(command, (error, stdout, stderr) => {
resolve(stdout ? stdout : stderr);
});
});
}
export { renderPhoenixView, renderPhoenixViewAsync };
require("@babel/register");
// Mock and bind CSS Modules
const hook = require("css-modules-require-hook");
hook({
generateScopedName: "[name]_[hash:base64:5]__[local]"
});
// Setup DOM
const { JSDOM } = require("jsdom");
const jsDOM = new JSDOM("");
global.window = jsDOM.window;
global.document = jsDOM.window.document;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment