Skip to content

Commit

Permalink
Add static rendering option to inferno-mobx
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Megidov committed Jul 8, 2017
1 parent ca16bf3 commit 0f8c8f1
Show file tree
Hide file tree
Showing 15 changed files with 1,229 additions and 603 deletions.
4 changes: 4 additions & 0 deletions packages/inferno-mobx/.editorconfig
@@ -0,0 +1,4 @@
# Tab indentation
[*.{js,ts,jsx,tsx}]
indent_style = tab
indent_size = 2
@@ -1,6 +1,7 @@
import { render } from "inferno";
import Component from "inferno-component";
import createElement from "inferno-create-element";
import createClass from "inferno-create-class";
import { connect, inject, Provider } from "inferno-mobx";
import { innerHTML } from "inferno-utils";

Expand Down Expand Up @@ -35,24 +36,21 @@ describe("MobX inject()", () => {
document.body.removeChild(container);
});

class TestComponent extends Component {
render({ testStore }) {
return createElement("span", null, testStore);
}
}
const TestComponent = ({ testStore }) => {
return createElement("span", null, testStore);
};

/*
it('should inject without second argument', () => {
it("should inject without second argument", () => {
class TestComponent extends Component {
static defaultProps = { hello: "world" };

class TestComponent extends Component {
static defaultProps = { hello: 'world' };
render() {
return 'Test';
}
};
const tryInject = () => inject()(TestComponent);
//expect(tryInject).to.not.throw(Error);
});*/
render() {
return "Test";
}
}
const tryInject = () => inject()(TestComponent);
expect(tryInject).not.toThrowError(Error);
});

it("should fail if store is not provided", () => {
function App() {
Expand Down Expand Up @@ -102,15 +100,14 @@ describe("MobX inject()", () => {
});

it("should create class with injected stores", () => {
class TestClass extends Component {
static defaultProps = {
world: "world"
};

const TestClass = createClass({
displayName: "TestClass",
render({ hello, world }) {
return createElement("span", null, hello + " " + world);
}
}
});

TestClass.defaultProps = { world: "world" };

function App() {
return createElement(
Expand Down
235 changes: 235 additions & 0 deletions packages/inferno-mobx/__tests__/context.spec.js
@@ -0,0 +1,235 @@
import createClass from "inferno-create-class";
import { observer, Provider } from "inferno-mobx";
import { innerHTML } from "inferno-utils";
import { observable } from "mobx";
import { renderComponent } from "./helpers";

describe("observer based context", () => {
let container = null;
let render = null;

beforeEach(function() {
container = document.createElement("div");
container.style.display = "none";
document.body.appendChild(container);
render = renderComponent.bind(this, container);
});

afterEach(function() {
render(null);
document.body.removeChild(container);
});

it("basic context", () => {
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = () => <C />;
const A = () =>
<Provider foo="bar">
<B />
</Provider>;
const wrapper = render(<A />, container);

expect(wrapper.find("div").text()).toBe("context:bar");
});

it("props override context", () => {
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = () => <C foo={42} />;
const A = () =>
<Provider foo="bar">
<B />
</Provider>;
const wrapper = render(<A />);

expect(wrapper.find("div").text()).toBe("context:42");
});

it("overriding stores is supported", () => {
const C = observer(
["foo", "bar"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
{this.props.bar}
</div>
);
}
})
);
const B = () => <C />;
const A = () =>
<Provider foo="bar" bar={1337}>
<div>
<span>
<B />
</span>
<section>
<Provider foo={42}>
<B />
</Provider>
</section>
</div>
</Provider>;
const wrapper = render(<A />);

expect(wrapper.find("span").text()).toBe("context:bar1337");
expect(wrapper.find("section").text()).toBe("context:421337");
});

it("store should be available", () => {
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = () => <C />;
const A = () =>
<Provider baz={42}>
<B />
</Provider>;

expect(() => render(<A />)).toThrowError(
/Store 'foo' is not available! Make sure it is provided by some Provider/
);
});

it("store is not required if prop is available", () => {
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = () => <C foo="bar" />;
const wrapper = render(<B />);
expect(wrapper.find("div").text()).toBe("context:bar");
});

it("warning is printed when changing stores (half-tested)", () => {
// Since we are using `warning` function, we need a different way to test warnings
// let msg = null;
// const baseWarn = console.warn;
// console.warn = m => msg = m;
const a = observable(3);
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = observer(
createClass({
render: () => <C />
})
);
const A = observer(
createClass({
render: () =>
<section>
<span>{a.get()}</span>,
<Provider foo={a.get()}>
<B />
</Provider>
</section>
})
);
const wrapper = render(<A />);

expect(wrapper.find("span").text()).toBe("3");
expect(wrapper.find("div").text()).toBe("context:3");
a.set(42);
expect(wrapper.find("span").text()).toBe("42");
expect(wrapper.find("div").text()).toBe("context:3");
// expect(msg).toBe('MobX Provider: Provided store \'foo\' has changed. Please avoid replacing stores as the change might not propagate to all children');
// console.warn = baseWarn;
});

it("warning is not printed when changing stores, but suppressed explicitly (half-tested)", () => {
// Since we are using `warning` function, we need a different way to test warnings
// let msg = null;
// const baseWarn = console.warn;
// console.warn = m => msg = m;
const a = observable(3);
const C = observer(
["foo"],
createClass({
render() {
return (
<div>
context:{this.props.foo}
</div>
);
}
})
);
const B = observer(
createClass({
render: () => <C />
})
);
const A = observer(
createClass({
render: () =>
<section>
<span>{a.get()}</span>,
<Provider foo={a.get()} suppressChangedStoreWarning>
<B />
</Provider>
</section>
})
);
const wrapper = render(<A />);

expect(wrapper.find("span").text()).toBe("3");
expect(wrapper.find("div").text()).toBe("context:3");
a.set(42);
expect(wrapper.find("span").text()).toBe("42");
expect(wrapper.find("div").text()).toBe("context:3");

// expect(msg).toBe(null);
// console.warn = baseWarn;
});
});
36 changes: 36 additions & 0 deletions packages/inferno-mobx/__tests__/helpers.js
@@ -0,0 +1,36 @@
import { render as infernoRender } from "inferno";

function getTextFromNode(node, addSpaces) {
let i, result, text, child;
result = "";
for (i = 0; i < node.childNodes.length; i++) {
child = node.childNodes[i];
text = null;
if (child.nodeType === 1) {
text = getTextFromNode(child, addSpaces);
} else if (child.nodeType === 3) {
text = child.nodeValue;
}
if (text) {
if (addSpaces && /\S$/.test(result) && /^\S/.test(text))
text = " " + text;
result += text;
}
}
return result;
}

export function renderComponent(container, component) {
infernoRender(component, container);
return {
selector: "",
find(selector) {
this.selector = selector;
return this;
},
text() {
const element = container.querySelector(this.selector);
return getTextFromNode(element);
}
};
}

0 comments on commit 0f8c8f1

Please sign in to comment.