Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function project(displayName, config) {
transform: {
"\\.ts$": "ts-jest"
},
coveragePathIgnorePatterns: ["/__tests__/"]
coveragePathIgnorePatterns: ["/__tests__/"],
transformIgnorePatterns: ["node_modules/(?!(marko)/)"]
};
}
67 changes: 67 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"jest-dom": "^3.5.0",
"lint-staged": "^8.2.1",
"marko": "^4.18.2",
"marko-widgets": "^7.0.1",
"prettier": "^1.18.2",
"ts-jest": "^24.0.2",
"tslint": "^5.18.0",
Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/fixtures/legacy-counter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = require("marko-widgets").defineComponent({
template: require("./template.marko"),
getInitialState() {
return { count: 0 };
},

increment() {
this.setState("count", this.state.count + 1);
}
});
4 changes: 4 additions & 0 deletions src/__tests__/fixtures/legacy-counter/template.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div w-bind>
Value: ${data.count}
<button w-on-click='increment'>Increment</button>
</div>
10 changes: 10 additions & 0 deletions src/__tests__/render.browser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "jest-dom/extend-expect";
import { render, fireEvent, wait, cleanup } from "..";
import Counter from "./fixtures/counter.marko";
import LegacyCounter from "./fixtures/legacy-counter";
import UpdateCounter from "./fixtures/update-counter.marko";
import HelloWorld from "./fixtures/hello-world.marko";
import HelloName from "./fixtures/hello-name.marko";
Expand All @@ -17,6 +18,15 @@ test("renders interactive content in the document", async () => {
await wait(() => expect(getByText("Value: 1")));
});

test("renders interactive content in the document with Marko 3", async () => {
const { getByText } = await render(LegacyCounter);
expect(getByText(/Value: 0/)).toBeInTheDocument();

fireEvent.click(getByText("Increment"));

await wait(() => expect(getByText("Value: 1")));
});

test("can be rerendered with new input", async () => {
const { getByText, rerender } = await render(HelloName, { name: "Michael" });

Expand Down
9 changes: 9 additions & 0 deletions src/__tests__/render.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, fireEvent } from "..";
import Counter from "./fixtures/counter.marko";
import LegacyCounter from "./fixtures/legacy-counter";
import Clickable from "./fixtures/clickable.marko";
import HelloName from "./fixtures/hello-name.marko";

Expand All @@ -11,6 +12,14 @@ test("renders static content in a document without a browser context", async ()
);
});

test("renders static content from a Marko 3 component", async () => {
const { getByText } = await render(LegacyCounter);
expect(getByText("Value: 0")).toHaveProperty(
["ownerDocument", "defaultView"],
null
);
});

test("fails when rerendering", async () => {
const { rerender } = await render(HelloName, { name: "Michael" });
await expect(rerender({ name: "Dylan" })).rejects.toThrow(/cannot re-render/);
Expand Down
40 changes: 32 additions & 8 deletions src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,17 @@ export async function render<T extends Template>(
container = document.body.appendChild(document.createElement("div"))
} = options;

const result = await template.render(input);
const component = result.appendTo(container).getComponent();
// Doesn't use promise API so that we can support Marko v3
const renderResult = (await new Promise((resolve, reject) =>
template.render(input, (err, result) =>
err ? /* istanbul ignore next */ reject(err) : resolve(result)
)
)) as any;

const isV3 = !renderResult.getComponent;
const component = renderResult
.appendTo(container)
[isV3 ? /* istanbul ignore next */ "getWidget" : "getComponent"]();
const eventRecord: EventRecord = {};
mountedComponents.add({ container, component });

Expand Down Expand Up @@ -63,12 +72,23 @@ export async function render<T extends Template>(
},
rerender(newInput?: typeof input): Promise<void> {
return new Promise(resolve => {
component.once("update", () => resolve());

if (newInput) {
component.input = newInput;
/* istanbul ignore if */
if (isV3) {
component.once("render", () => resolve());

if (newInput) {
component.setProps(newInput);
} else {
component.setStateDirty("__forceUpdate__");
}
} else {
component.forceUpdate();
component.once("update", () => resolve());

if (newInput) {
component.input = newInput;
} else {
component.forceUpdate();
}
}
});
},
Expand All @@ -85,13 +105,17 @@ export async function render<T extends Template>(
}
},
...within((container as any) as HTMLElement)
};
} as const;
}

export function cleanup() {
mountedComponents.forEach(destroyComponent);
}

export type RenderResult = Parameters<
NonNullable<Parameters<ReturnType<typeof render>["then"]>[0]>
>[0];

function destroyComponent({ container, component }) {
component.destroy();

Expand Down
18 changes: 15 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ export * from "@testing-library/dom";

export async function render<T extends Template>(
template: T,
input: Parameters<T["render"]>[0] = {},
input: Parameters<T["renderToString"]>[0] = {},
options?: RenderOptions
) {
const html = String(await template.render(input));
// Doesn't use promise API so that we can support Marko v3
const renderMethod = template.renderToString ? "renderToString" : "render";
const html = String(
await new Promise((resolve, reject) =>
template[renderMethod](input, (err, result) =>
err ? /* istanbul ignore next */ reject(err) : resolve(result)
)
)
);
const container = JSDOM.fragment(html);
(container as any).outerHTML = html; // Fixes prettyDOM for container

Expand Down Expand Up @@ -43,8 +51,12 @@ export async function render<T extends Template>(
}
},
...within((container as any) as HTMLElement)
};
} as const;
}

/* istanbul ignore next: There is no cleanup for SSR. */
export function cleanup() {}

export type RenderResult = Parameters<
NonNullable<Parameters<ReturnType<typeof render>["then"]>[0]>
>[0];
6 changes: 5 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ export interface RenderOptions {
}

export interface Template {
render(input: unknown): Promise<any>;
renderToString(
input: unknown,
cb: (err: Error | null, result: any) => void
): any;
render(input: unknown, cb: (err: Error | null, result: any) => void): any;
}

export const INTERNAL_EVENTS = [
Expand Down