3319 lines
122 KiB
JavaScript
3319 lines
122 KiB
JavaScript
/* eslint max-nested-callbacks: [1, 7] */
|
|
/* eslint-disable import/no-named-as-default */
|
|
|
|
import React from "react";
|
|
import ReactDOM from "react-dom";
|
|
import ReactServer from "react-dom/server";
|
|
import {Helmet} from "../src/Helmet";
|
|
import {requestAnimationFrame} from "../src/HelmetUtils.js";
|
|
|
|
const HELMET_ATTRIBUTE = "data-react-helmet";
|
|
|
|
describe("Helmet", () => {
|
|
let headElement;
|
|
|
|
const container = document.createElement("div");
|
|
|
|
beforeEach(() => {
|
|
headElement =
|
|
headElement || document.head || document.querySelector("head");
|
|
|
|
// resets DOM after each run
|
|
headElement.innerHTML = "";
|
|
});
|
|
|
|
afterEach(() => {
|
|
ReactDOM.unmountComponentAtNode(container);
|
|
});
|
|
|
|
describe("api", () => {
|
|
describe("title", () => {
|
|
it("updates page title", done => {
|
|
ReactDOM.render(
|
|
<Helmet defaultTitle={"Fallback"} title={"Test Title"} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Test Title");
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("updates page title with multiple children", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet title={"Test Title"} />
|
|
<Helmet title={"Child One Title"} />
|
|
<Helmet title={"Child Two Title"} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Child Two Title");
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets title based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet title={"Main Title"} />
|
|
<Helmet title={"Nested Title"} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Nested Title");
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets title using deepest nested component with a defined title", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet title={"Main Title"} />
|
|
<Helmet />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Main Title");
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("uses defaultTitle if no title is defined", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
defaultTitle={"Fallback"}
|
|
title={""}
|
|
titleTemplate={
|
|
"This is a %s of the titleTemplate feature"
|
|
}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Fallback");
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("uses a titleTemplate if defined", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
defaultTitle={"Fallback"}
|
|
title={"Test"}
|
|
titleTemplate={
|
|
"This is a %s of the titleTemplate feature"
|
|
}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(
|
|
"This is a Test of the titleTemplate feature"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("replaces multiple title strings in titleTemplate", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
title={"Test"}
|
|
titleTemplate={
|
|
"This is a %s of the titleTemplate feature. Another %s."
|
|
}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(
|
|
"This is a Test of the titleTemplate feature. Another Test."
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("uses a titleTemplate based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
title={"Test"}
|
|
titleTemplate={
|
|
"This is a %s of the titleTemplate feature"
|
|
}
|
|
/>
|
|
<Helmet
|
|
title={"Second Test"}
|
|
titleTemplate={
|
|
"A %s using nested titleTemplate attributes"
|
|
}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(
|
|
"A Second Test using nested titleTemplate attributes"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("merges deepest component title with nearest upstream titleTemplate", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
title={"Test"}
|
|
titleTemplate={
|
|
"This is a %s of the titleTemplate feature"
|
|
}
|
|
/>
|
|
<Helmet title={"Second Test"} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(
|
|
"This is a Second Test of the titleTemplate feature"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("renders dollar characters in a title correctly when titleTemplate present", done => {
|
|
const dollarTitle = "te$t te$$t te$$$t te$$$$t";
|
|
|
|
ReactDOM.render(
|
|
<Helmet
|
|
title={dollarTitle}
|
|
titleTemplate={"This is a %s"}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(
|
|
"This is a te$t te$$t te$$$t te$$$$t"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not encode all characters with HTML character entity equivalents", done => {
|
|
const chineseTitle = "膣膗 鍆錌雔";
|
|
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet title={chineseTitle} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal(chineseTitle);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("page title with prop itemprop", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
defaultTitle={"Fallback"}
|
|
title={"Test Title with itemProp"}
|
|
titleAttributes={{itemprop: "name"}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const titleTag = document.getElementsByTagName("title")[0];
|
|
expect(document.title).to.equal("Test Title with itemProp");
|
|
expect(titleTag.getAttribute("itemprop")).to.equal("name");
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("title attributes", () => {
|
|
beforeEach(() => {
|
|
headElement.innerHTML = `<title>Test Title</title>`;
|
|
});
|
|
|
|
it("update title attributes", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
titleAttributes={{
|
|
itemprop: "name"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const titleTag = document.getElementsByTagName("title")[0];
|
|
expect(titleTag.getAttribute("itemprop")).to.equal("name");
|
|
expect(titleTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"itemprop"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets attributes based on the deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
titleAttributes={{
|
|
lang: "en",
|
|
hidden: undefined
|
|
}}
|
|
/>
|
|
<Helmet
|
|
titleAttributes={{
|
|
lang: "ja"
|
|
}}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const titleTag = document.getElementsByTagName("title")[0];
|
|
expect(titleTag.getAttribute("lang")).to.equal("ja");
|
|
expect(titleTag.getAttribute("hidden")).to.equal("");
|
|
expect(titleTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"lang,hidden"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("handles valueless attributes", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
titleAttributes={{
|
|
hidden: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const titleTag = document.getElementsByTagName("title")[0];
|
|
expect(titleTag.getAttribute("hidden")).to.equal("");
|
|
expect(titleTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"hidden"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears title attributes that are handled within helmet", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
titleAttributes={{
|
|
lang: "en",
|
|
hidden: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const titleTag = document.getElementsByTagName(
|
|
"title"
|
|
)[0];
|
|
expect(titleTag.getAttribute("lang")).to.be.null;
|
|
expect(titleTag.getAttribute("hidden")).to.be.null;
|
|
expect(
|
|
titleTag.getAttribute(HELMET_ATTRIBUTE)
|
|
).to.equal(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("html attributes", () => {
|
|
it("updates html attributes", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
class: "myClassName",
|
|
lang: "en"
|
|
}}
|
|
lang="en"
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName("html")[0];
|
|
expect(htmlTag.getAttribute("class")).to.equal(
|
|
"myClassName"
|
|
);
|
|
expect(htmlTag.getAttribute("lang")).to.equal("en");
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"class,lang"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets attributes based on the deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "en"
|
|
}}
|
|
/>
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "ja"
|
|
}}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName("html")[0];
|
|
expect(htmlTag.getAttribute("lang")).to.equal("ja");
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"lang"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("handles valueless attributes", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
amp: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName("html")[0];
|
|
expect(htmlTag.getAttribute("amp")).to.equal("");
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"amp"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears html attributes that are handled within helmet", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "en",
|
|
amp: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("lang")).to.be.null;
|
|
expect(htmlTag.getAttribute("amp")).to.be.null;
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
null
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("updates with multiple additions and removals - overwrite and new", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "en",
|
|
amp: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "ja",
|
|
id: "html-tag",
|
|
title: "html tag"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("amp")).to.equal(null);
|
|
expect(htmlTag.getAttribute("lang")).to.equal("ja");
|
|
expect(htmlTag.getAttribute("id")).to.equal("html-tag");
|
|
expect(htmlTag.getAttribute("title")).to.equal(
|
|
"html tag"
|
|
);
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"lang,id,title"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("updates with multiple additions and removals - all new", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "en",
|
|
amp: undefined
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
id: "html-tag",
|
|
title: "html tag"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("amp")).to.equal(null);
|
|
expect(htmlTag.getAttribute("lang")).to.equal(null);
|
|
expect(htmlTag.getAttribute("id")).to.equal("html-tag");
|
|
expect(htmlTag.getAttribute("title")).to.equal(
|
|
"html tag"
|
|
);
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"id,title"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
context("initialized outside of helmet", () => {
|
|
before(() => {
|
|
const htmlTag = document.getElementsByTagName("html")[0];
|
|
htmlTag.setAttribute("test", "test");
|
|
});
|
|
|
|
it("attributes are not cleared", done => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("test")).to.equal("test");
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
null
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("attributes are overwritten if specified in helmet", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
test: "helmet-attr"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("test")).to.equal(
|
|
"helmet-attr"
|
|
);
|
|
expect(htmlTag.getAttribute(HELMET_ATTRIBUTE)).to.equal(
|
|
"test"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("attributes are cleared once managed in helmet", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
test: "helmet-attr"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const htmlTag = document.getElementsByTagName(
|
|
"html"
|
|
)[0];
|
|
expect(htmlTag.getAttribute("test")).to.equal(null);
|
|
expect(
|
|
htmlTag.getAttribute(HELMET_ATTRIBUTE)
|
|
).to.equal(null);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("onChangeClientState", () => {
|
|
it("when handling client state change, calls the function with new state, addedTags and removedTags ", done => {
|
|
const spy = sinon.spy();
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
base={{href: "http://mysite.com/"}}
|
|
link={[
|
|
{
|
|
href: "http://localhost/helmet",
|
|
rel: "canonical"
|
|
}
|
|
]}
|
|
meta={[{charset: "utf-8"}]}
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
title={"Main Title"}
|
|
onChangeClientState={spy}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.called).to.equal(true);
|
|
const newState = spy.getCall(0).args[0];
|
|
const addedTags = spy.getCall(0).args[1];
|
|
const removedTags = spy.getCall(0).args[2];
|
|
|
|
expect(newState).to.contain({title: "Main Title"});
|
|
expect(newState.baseTag).to.contain({
|
|
href: "http://mysite.com/"
|
|
});
|
|
expect(newState.metaTags).to.contain({charset: "utf-8"});
|
|
expect(newState.linkTags).to.contain({
|
|
href: "http://localhost/helmet",
|
|
rel: "canonical"
|
|
});
|
|
expect(newState.scriptTags).to.contain({
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
});
|
|
|
|
expect(addedTags).to.have.property("baseTag");
|
|
expect(addedTags.baseTag).to.have.deep.property("[0]");
|
|
expect(addedTags.baseTag[0].outerHTML).to.equal(
|
|
`<base href="http://mysite.com/" data-react-helmet="true">`
|
|
);
|
|
|
|
expect(addedTags).to.have.property("metaTags");
|
|
expect(addedTags.metaTags).to.have.deep.property("[0]");
|
|
expect(addedTags.metaTags[0].outerHTML).to.equal(
|
|
`<meta charset="utf-8" data-react-helmet="true">`
|
|
);
|
|
|
|
expect(addedTags).to.have.property("linkTags");
|
|
expect(addedTags.linkTags).to.have.deep.property("[0]");
|
|
expect(addedTags.linkTags[0].outerHTML).to.equal(
|
|
`<link href="http://localhost/helmet" rel="canonical" data-react-helmet="true">`
|
|
);
|
|
|
|
expect(addedTags).to.have.property("scriptTags");
|
|
expect(addedTags.scriptTags).to.have.deep.property("[0]");
|
|
expect(addedTags.scriptTags[0].outerHTML).to.equal(
|
|
`<script src="http://localhost/test.js" type="text/javascript" data-react-helmet="true"></script>`
|
|
);
|
|
|
|
expect(removedTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("calls the deepest defined callback with the deepest state", done => {
|
|
const spy = sinon.spy();
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
title={"Main Title"}
|
|
onChangeClientState={spy}
|
|
/>
|
|
<Helmet title={"Deeper Title"} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.callCount).to.equal(1);
|
|
expect(spy.getCall(0).args[0]).to.contain({
|
|
title: "Deeper Title"
|
|
});
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("base tag", () => {
|
|
it("updates base tag", done => {
|
|
ReactDOM.render(
|
|
<Helmet base={{href: "http://mysite.com/"}} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`base[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
const filteredTags = [].slice
|
|
.call(existingTags)
|
|
.filter(tag => {
|
|
return (
|
|
tag.getAttribute("href") ===
|
|
"http://mysite.com/"
|
|
);
|
|
});
|
|
|
|
expect(filteredTags.length).to.equal(1);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears the base tag if one is not specified", done => {
|
|
ReactDOM.render(
|
|
<Helmet base={{href: "http://mysite.com/"}} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`base[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'href' are not accepted", done => {
|
|
ReactDOM.render(
|
|
<Helmet base={{property: "won't work"}} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`base[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets base tag based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet base={{href: "http://mysite.com/"}} />
|
|
<Helmet base={{href: "http://mysite.com/public"}} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`base[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const firstTag = Array.prototype.slice.call(
|
|
existingTags
|
|
)[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://mysite.com/public"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<base href="http://mysite.com/public" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute is null", done => {
|
|
ReactDOM.render(<Helmet base={{href: undefined}} />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`base[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("meta tags", () => {
|
|
it("updates meta tags", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{charset: "utf-8"},
|
|
{
|
|
name: "description",
|
|
content: "Test description"
|
|
},
|
|
{
|
|
"http-equiv": "content-type",
|
|
content: "text/html"
|
|
},
|
|
{property: "og:type", content: "article"},
|
|
{itemprop: "name", content: "Test name itemprop"}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
const filteredTags = [].slice
|
|
.call(existingTags)
|
|
.filter(tag => {
|
|
return (
|
|
tag.getAttribute("charset") === "utf-8" ||
|
|
(tag.getAttribute("name") === "description" &&
|
|
tag.getAttribute("content") ===
|
|
"Test description") ||
|
|
(tag.getAttribute("http-equiv") ===
|
|
"content-type" &&
|
|
tag.getAttribute("content") ===
|
|
"text/html") ||
|
|
(tag.getAttribute("itemprop") === "name" &&
|
|
tag.getAttribute("content") ===
|
|
"Test name itemprop")
|
|
);
|
|
});
|
|
|
|
expect(filteredTags.length).to.be.at.least(4);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears all meta tags if none are specified", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{name: "description", content: "Test description"}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'name', 'http-equiv', 'property', 'charset', or 'itemprop' are not accepted", done => {
|
|
ReactDOM.render(
|
|
<Helmet meta={[{href: "won't work"}]} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets meta tags based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
meta={[
|
|
{charset: "utf-8"},
|
|
{
|
|
name: "description",
|
|
content: "Test description"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Inner description"
|
|
},
|
|
{name: "keywords", content: "test,meta,tags"}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
const thirdTag = existingTags[2];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(3);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("charset")).to.equal("utf-8");
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<meta charset="utf-8" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(secondTag.getAttribute("content")).to.equal(
|
|
"Inner description"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Inner description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[2]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(thirdTag).to.have.property("getAttribute");
|
|
expect(thirdTag.getAttribute("name")).to.equal("keywords");
|
|
expect(thirdTag.getAttribute("content")).to.equal(
|
|
"test,meta,tags"
|
|
);
|
|
expect(thirdTag.outerHTML).to.equal(
|
|
`<meta name="keywords" content="test,meta,tags" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("allows duplicate meta tags if specified in the same component", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Test description"
|
|
},
|
|
{
|
|
name: "description",
|
|
content: "Duplicate description"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.equal(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(firstTag.getAttribute("content")).to.equal(
|
|
"Test description"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Test description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(secondTag.getAttribute("content")).to.equal(
|
|
"Duplicate description"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Duplicate description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("overrides duplicate meta tags with single meta tag in a nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Test description"
|
|
},
|
|
{
|
|
name: "description",
|
|
content: "Duplicate description"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Inner description"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(firstTag.getAttribute("content")).to.equal(
|
|
"Inner description"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Inner description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("overrides single meta tag with duplicate meta tags in a nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Test description"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: "Inner description"
|
|
},
|
|
{
|
|
name: "description",
|
|
content: "Inner duplicate description"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.equal(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(firstTag.getAttribute("content")).to.equal(
|
|
"Inner description"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Inner description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(secondTag.getAttribute("content")).to.equal(
|
|
"Inner duplicate description"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<meta name="description" content="Inner duplicate description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: undefined,
|
|
content: "Inner duplicate description"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("fails gracefully when meta is wrong shape", done => {
|
|
const error = sinon.stub(console, "error");
|
|
const warn = sinon.stub(console, "warn");
|
|
|
|
ReactDOM.render(
|
|
<Helmet meta={{name: "title", content: "some title"}} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
expect(error.called).to.be.true;
|
|
expect(warn.called).to.be.true;
|
|
|
|
const [warning] = warn.getCall(0).args;
|
|
expect(warning).to.equal(
|
|
`Helmet: meta should be of type "Array". Instead found type "object"`
|
|
);
|
|
|
|
error.restore();
|
|
warn.restore();
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("link tags", () => {
|
|
it("updates link tags", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/helmet",
|
|
rel: "canonical"
|
|
},
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
const filteredTags = [].slice
|
|
.call(existingTags)
|
|
.filter(tag => {
|
|
return (
|
|
(tag.getAttribute("href") ===
|
|
"http://localhost/style.css" &&
|
|
tag.getAttribute("rel") === "stylesheet" &&
|
|
tag.getAttribute("type") === "text/css") ||
|
|
(tag.getAttribute("href") ===
|
|
"http://localhost/helmet" &&
|
|
tag.getAttribute("rel") === "canonical")
|
|
);
|
|
});
|
|
|
|
expect(filteredTags.length).to.be.at.least(2);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears all link tags if none are specified", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/helmet",
|
|
rel: "canonical"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(
|
|
tagNodes
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'href' or 'rel' are not accepted, even if they are valid for other tags", done => {
|
|
ReactDOM.render(
|
|
<Helmet link={[{"http-equiv": "won't work"}]} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("tags 'rel' and 'href' properly use 'rel' as the primary identification for this tag, regardless of ordering", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/helmet",
|
|
rel: "canonical"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet/new"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/helmet/newest",
|
|
rel: "canonical"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/newest"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link href="http://localhost/helmet/newest" rel="canonical" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("tags with rel='stylesheet' uses the href as the primary identification of the tag, regardless of ordering", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css",
|
|
media: "all"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "stylesheet",
|
|
href: "http://localhost/inner.css",
|
|
type: "text/css",
|
|
media: "all"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.equal(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/style.css"
|
|
);
|
|
expect(firstTag.getAttribute("rel")).to.equal("stylesheet");
|
|
expect(firstTag.getAttribute("type")).to.equal("text/css");
|
|
expect(firstTag.getAttribute("media")).to.equal("all");
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link href="http://localhost/style.css" rel="stylesheet" type="text/css" media="all" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("rel")).to.equal(
|
|
"stylesheet"
|
|
);
|
|
expect(secondTag.getAttribute("href")).to.equal(
|
|
"http://localhost/inner.css"
|
|
);
|
|
expect(secondTag.getAttribute("type")).to.equal("text/css");
|
|
expect(secondTag.getAttribute("media")).to.equal("all");
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<link rel="stylesheet" href="http://localhost/inner.css" type="text/css" media="all" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets link tags based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet"
|
|
},
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css",
|
|
media: "all"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href:
|
|
"http://localhost/helmet/innercomponent"
|
|
},
|
|
{
|
|
href: "http://localhost/inner.css",
|
|
rel: "stylesheet",
|
|
type: "text/css",
|
|
media: "all"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
const thirdTag = existingTags[2];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.at.least(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/style.css"
|
|
);
|
|
expect(firstTag.getAttribute("rel")).to.equal("stylesheet");
|
|
expect(firstTag.getAttribute("type")).to.equal("text/css");
|
|
expect(firstTag.getAttribute("media")).to.equal("all");
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link href="http://localhost/style.css" rel="stylesheet" type="text/css" media="all" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/innercomponent"
|
|
);
|
|
expect(secondTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/innercomponent" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[2]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(thirdTag).to.have.property("getAttribute");
|
|
expect(thirdTag.getAttribute("href")).to.equal(
|
|
"http://localhost/inner.css"
|
|
);
|
|
expect(thirdTag.getAttribute("rel")).to.equal("stylesheet");
|
|
expect(thirdTag.getAttribute("type")).to.equal("text/css");
|
|
expect(thirdTag.getAttribute("media")).to.equal("all");
|
|
expect(thirdTag.outerHTML).to.equal(
|
|
`<link href="http://localhost/inner.css" rel="stylesheet" type="text/css" media="all" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("allows duplicate link tags if specified in the same component", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet"
|
|
},
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet/component"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.at.least(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(secondTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/component"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/component" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("overrides duplicate link tags with a single link tag in a nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet"
|
|
},
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet/component"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href:
|
|
"http://localhost/helmet/innercomponent"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/innercomponent"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/innercomponent" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("overrides single link tag with duplicate link tags in a nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
link={[
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet/component"
|
|
},
|
|
{
|
|
rel: "canonical",
|
|
href:
|
|
"http://localhost/helmet/innercomponent"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/component"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/component" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(secondTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/innercomponent"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/innercomponent" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{rel: "icon", sizes: "192x192", href: null},
|
|
{
|
|
rel: "canonical",
|
|
href: "http://localhost/helmet/component"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`link[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.be.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("rel")).to.equal("canonical");
|
|
expect(firstTag.getAttribute("href")).to.equal(
|
|
"http://localhost/helmet/component"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<link rel="canonical" href="http://localhost/helmet/component" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("script tags", () => {
|
|
it("updates script tags", done => {
|
|
const scriptInnerHTML = `
|
|
{
|
|
"@context": "http://schema.org",
|
|
"@type": "NewsArticle",
|
|
"url": "http://localhost/helmet"
|
|
}
|
|
`;
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
},
|
|
{
|
|
src: "http://localhost/test2.js",
|
|
type: "text/javascript"
|
|
},
|
|
{
|
|
type: "application/ld+json",
|
|
innerHTML: scriptInnerHTML
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.getElementsByTagName(
|
|
"script"
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
const filteredTags = [].slice
|
|
.call(existingTags)
|
|
.filter(tag => {
|
|
return (
|
|
(tag.getAttribute("src") ===
|
|
"http://localhost/test.js" &&
|
|
tag.getAttribute("type") ===
|
|
"text/javascript") ||
|
|
(tag.getAttribute("src") ===
|
|
"http://localhost/test2.js" &&
|
|
tag.getAttribute("type") ===
|
|
"text/javascript") ||
|
|
(tag.getAttribute("type") ===
|
|
"application/ld+json" &&
|
|
tag.innerHTML === scriptInnerHTML)
|
|
);
|
|
});
|
|
|
|
expect(filteredTags.length).to.be.at.least(3);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears all scripts tags if none are specified", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'src' are not accepted", done => {
|
|
ReactDOM.render(
|
|
<Helmet script={[{property: "won't work"}]} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets script tags based on deepest nested component", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test2.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
const firstTag = existingTags[0];
|
|
const secondTag = existingTags[1];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.at.least(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("src")).to.equal(
|
|
"http://localhost/test.js"
|
|
);
|
|
expect(firstTag.getAttribute("type")).to.equal(
|
|
"text/javascript"
|
|
);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<script src="http://localhost/test.js" type="text/javascript" ${HELMET_ATTRIBUTE}="true"></script>`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag).to.have.property("getAttribute");
|
|
expect(secondTag.getAttribute("src")).to.equal(
|
|
"http://localhost/test2.js"
|
|
);
|
|
expect(secondTag.getAttribute("type")).to.equal(
|
|
"text/javascript"
|
|
);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<script src="http://localhost/test2.js" type="text/javascript" ${HELMET_ATTRIBUTE}="true"></script>`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("sets undefined attribute values to empty strings", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "foo.js",
|
|
async: undefined
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTag = headElement.querySelector(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTag).to.not.equal(undefined);
|
|
expect(existingTag.outerHTML).to.be
|
|
.a("string")
|
|
.that.equals(
|
|
`<script src="foo.js" async="" ${HELMET_ATTRIBUTE}="true"></script>`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute (src) is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: undefined,
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute (innerHTML) is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
innerHTML: undefined
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("noscript tags", () => {
|
|
it("updates noscript tags", done => {
|
|
const noscriptInnerHTML = `<link rel="stylesheet" type="text/css" href="foo.css" />`;
|
|
ReactDOM.render(
|
|
<Helmet
|
|
noscript={[{id: "bar", innerHTML: noscriptInnerHTML}]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.getElementsByTagName(
|
|
"noscript"
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(1);
|
|
expect(
|
|
existingTags[0].innerHTML === noscriptInnerHTML &&
|
|
existingTags[0].id === "bar"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears all noscripts tags if none are specified", done => {
|
|
ReactDOM.render(<Helmet noscript={[{id: "bar"}]} />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`script[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'innerHTML' are not accepted", done => {
|
|
ReactDOM.render(
|
|
<Helmet noscript={[{property: "won't work"}]} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`noscript[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
noscript={[
|
|
{
|
|
innerHTML: undefined
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`noscript[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("style tags", () => {
|
|
it("updates style tags", done => {
|
|
const cssText1 = `
|
|
body {
|
|
background-color: green;
|
|
}
|
|
`;
|
|
const cssText2 = `
|
|
p {
|
|
font-size: 12px;
|
|
}
|
|
`;
|
|
ReactDOM.render(
|
|
<Helmet
|
|
style={[
|
|
{
|
|
type: "text/css",
|
|
cssText: cssText1
|
|
},
|
|
{
|
|
cssText: cssText2
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`style[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
|
|
const [firstTag, secondTag] = existingTags;
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.be.equal(2);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(firstTag).to.have.property("getAttribute");
|
|
expect(firstTag.getAttribute("type")).to.equal("text/css");
|
|
expect(firstTag.innerHTML).to.equal(cssText1);
|
|
expect(firstTag.outerHTML).to.equal(
|
|
`<style type="text/css" ${HELMET_ATTRIBUTE}="true">${cssText1}</style>`
|
|
);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[1]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(secondTag.innerHTML).to.equal(cssText2);
|
|
expect(secondTag.outerHTML).to.equal(
|
|
`<style ${HELMET_ATTRIBUTE}="true">${cssText2}</style>`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("clears all style tags if none are specified", done => {
|
|
const cssText = `
|
|
body {
|
|
background-color: green;
|
|
}
|
|
`;
|
|
ReactDOM.render(
|
|
<Helmet
|
|
style={[
|
|
{
|
|
type: "text/css",
|
|
cssText
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
ReactDOM.render(<Helmet />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`style[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("tags without 'cssText' are not accepted", done => {
|
|
ReactDOM.render(
|
|
<Helmet style={[{property: "won't work"}]} />,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`style[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
expect(existingTags.length).to.equal(0);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not render tag when primary attribute is null", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
style={[
|
|
{
|
|
cssText: undefined
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const tagNodes = headElement.querySelectorAll(
|
|
`style[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTags = Array.prototype.slice.call(tagNodes);
|
|
expect(existingTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("deferred tags", () => {
|
|
beforeEach(() => {
|
|
window.__spy__ = sinon.spy();
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete window.__spy__;
|
|
});
|
|
|
|
it("executes synchronously when defer={true} and async otherwise", done => {
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet
|
|
defer={false}
|
|
script={[
|
|
{
|
|
innerHTML: `window.__spy__(1)`
|
|
}
|
|
]}
|
|
/>
|
|
<Helmet
|
|
script={[
|
|
{
|
|
innerHTML: `window.__spy__(2)`
|
|
}
|
|
]}
|
|
/>
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
expect(window.__spy__.callCount).to.equal(1);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(window.__spy__.callCount).to.equal(2);
|
|
expect(window.__spy__.args).to.deep.equal([[1], [2]]);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("server", () => {
|
|
const stringifiedHtmlAttributes = `lang="ga" class="myClassName"`;
|
|
const stringifiedTitle = `<title ${HELMET_ATTRIBUTE}="true">Dangerous <script> include</title>`;
|
|
const unEncodedStringifiedTitle = `<title ${HELMET_ATTRIBUTE}="true">This is text and & and '.</title>`;
|
|
const stringifiedTitleWithItemprop = `<title ${HELMET_ATTRIBUTE}="true" itemprop="name">Title with Itemprop</title>`;
|
|
const stringifiedBaseTag = `<base ${HELMET_ATTRIBUTE}="true" target="_blank" href="http://localhost/"/>`;
|
|
|
|
const stringifiedMetaTags = [
|
|
`<meta ${HELMET_ATTRIBUTE}="true" charset="utf-8"/>`,
|
|
`<meta ${HELMET_ATTRIBUTE}="true" name="description" content="Test description & encoding of special characters like ' " > < \`"/>`,
|
|
`<meta ${HELMET_ATTRIBUTE}="true" http-equiv="content-type" content="text/html"/>`,
|
|
`<meta ${HELMET_ATTRIBUTE}="true" property="og:type" content="article"/>`,
|
|
`<meta ${HELMET_ATTRIBUTE}="true" itemprop="name" content="Test name itemprop"/>`
|
|
].join("");
|
|
|
|
const stringifiedLinkTags = [
|
|
`<link ${HELMET_ATTRIBUTE}="true" href="http://localhost/helmet" rel="canonical"/>`,
|
|
`<link ${HELMET_ATTRIBUTE}="true" href="http://localhost/style.css" rel="stylesheet" type="text/css"/>`
|
|
].join("");
|
|
|
|
const stringifiedScriptTags = [
|
|
`<script ${HELMET_ATTRIBUTE}="true" src="http://localhost/test.js" type="text/javascript"></script>`,
|
|
`<script ${HELMET_ATTRIBUTE}="true" src="http://localhost/test2.js" type="text/javascript"></script>`
|
|
].join("");
|
|
|
|
const stringifiedNoscriptTags = [
|
|
`<noscript ${HELMET_ATTRIBUTE}="true" id="foo"><link rel="stylesheet" type="text/css" href="/style.css" /></noscript>`,
|
|
`<noscript ${HELMET_ATTRIBUTE}="true" id="bar"><link rel="stylesheet" type="text/css" href="/style2.css" /></noscript>`
|
|
].join("");
|
|
|
|
const stringifiedStyleTags = [
|
|
`<style ${HELMET_ATTRIBUTE}="true" type="text/css">body {background-color: green;}</style>`,
|
|
`<style ${HELMET_ATTRIBUTE}="true" type="text/css">p {font-size: 12px;}</style>`
|
|
].join("");
|
|
|
|
before(() => {
|
|
Helmet.canUseDOM = false;
|
|
});
|
|
|
|
it("provides initial values if no state is found", () => {
|
|
let head = Helmet.rewind();
|
|
head = Helmet.rewind();
|
|
|
|
expect(head.meta).to.exist;
|
|
expect(head.meta).to.respondTo("toString");
|
|
|
|
expect(head.meta.toString()).to.equal("");
|
|
});
|
|
|
|
it("encodes special characters in title", () => {
|
|
ReactDOM.render(
|
|
<Helmet title="Dangerous <script> include" />,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
|
|
expect(head.title.toString()).to.equal(stringifiedTitle);
|
|
});
|
|
|
|
it("opts out of string encoding", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
encodeSpecialCharacters={false}
|
|
title={"This is text and & and '."}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
|
|
expect(head.title.toString()).to.equal(unEncodedStringifiedTitle);
|
|
});
|
|
|
|
it("renders title as React component", () => {
|
|
ReactDOM.render(
|
|
<Helmet title={"Dangerous <script> include"} />,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toComponent");
|
|
|
|
const titleComponent = head.title.toComponent();
|
|
|
|
expect(titleComponent).to.be.an("array").that.has.length.of(1);
|
|
|
|
titleComponent.forEach(title => {
|
|
expect(title).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "title");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{titleComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedTitle}</div>`);
|
|
});
|
|
|
|
it("renders title with itemprop name as React component", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
title={"Title with Itemprop"}
|
|
titleAttributes={{itemprop: "name"}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toComponent");
|
|
|
|
const titleComponent = head.title.toComponent();
|
|
|
|
expect(titleComponent).to.be.an("array").that.has.length.of(1);
|
|
|
|
titleComponent.forEach(title => {
|
|
expect(title).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "title");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{titleComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedTitleWithItemprop}</div>`);
|
|
});
|
|
|
|
it("renders base tag as React component", () => {
|
|
ReactDOM.render(
|
|
<Helmet base={{target: "_blank", href: "http://localhost/"}} />,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.base).to.exist;
|
|
expect(head.base).to.respondTo("toComponent");
|
|
|
|
const baseComponent = head.base.toComponent();
|
|
|
|
expect(baseComponent).to.be.an("array").that.has.length.of(1);
|
|
|
|
baseComponent.forEach(base => {
|
|
expect(base).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "base");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{baseComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedBaseTag}</div>`);
|
|
});
|
|
|
|
it("renders meta tags as React components", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{charset: "utf-8"},
|
|
{
|
|
name: "description",
|
|
content:
|
|
"Test description & encoding of special characters like ' \" > < `"
|
|
},
|
|
{"http-equiv": "content-type", content: "text/html"},
|
|
{property: "og:type", content: "article"},
|
|
{itemprop: "name", content: "Test name itemprop"}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.meta).to.exist;
|
|
expect(head.meta).to.respondTo("toComponent");
|
|
|
|
const metaComponent = head.meta.toComponent();
|
|
|
|
expect(metaComponent).to.be.an("array").that.has.length.of(5);
|
|
|
|
metaComponent.forEach(meta => {
|
|
expect(meta).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "meta");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{metaComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedMetaTags}</div>`);
|
|
});
|
|
|
|
it("renders link tags as React components", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{href: "http://localhost/helmet", rel: "canonical"},
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.link).to.exist;
|
|
expect(head.link).to.respondTo("toComponent");
|
|
|
|
const linkComponent = head.link.toComponent();
|
|
|
|
expect(linkComponent).to.be.an("array").that.has.length.of(2);
|
|
|
|
linkComponent.forEach(link => {
|
|
expect(link).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "link");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{linkComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedLinkTags}</div>`);
|
|
});
|
|
|
|
it("renders script tags as React components", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
},
|
|
{
|
|
src: "http://localhost/test2.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.script).to.exist;
|
|
expect(head.script).to.respondTo("toComponent");
|
|
|
|
const scriptComponent = head.script.toComponent();
|
|
|
|
expect(scriptComponent).to.be.an("array").that.has.length.of(2);
|
|
|
|
scriptComponent.forEach(script => {
|
|
expect(script).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "script");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{scriptComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedScriptTags}</div>`);
|
|
});
|
|
|
|
it("renders noscript tags as React components", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
noscript={[
|
|
{
|
|
id: "foo",
|
|
innerHTML:
|
|
'<link rel="stylesheet" type="text/css" href="/style.css" />'
|
|
},
|
|
{
|
|
id: "bar",
|
|
innerHTML:
|
|
'<link rel="stylesheet" type="text/css" href="/style2.css" />'
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.noscript).to.exist;
|
|
expect(head.noscript).to.respondTo("toComponent");
|
|
|
|
const noscriptComponent = head.noscript.toComponent();
|
|
|
|
expect(noscriptComponent).to.be.an("array").that.has.length.of(2);
|
|
|
|
noscriptComponent.forEach(noscript => {
|
|
expect(noscript).to.be
|
|
.an("object")
|
|
.that.contains.property("type", "noscript");
|
|
});
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{noscriptComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedNoscriptTags}</div>`);
|
|
});
|
|
|
|
it("renders style tags as React components", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
style={[
|
|
{
|
|
type: "text/css",
|
|
cssText: `body {background-color: green;}`
|
|
},
|
|
{
|
|
type: "text/css",
|
|
cssText: `p {font-size: 12px;}`
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.style).to.exist;
|
|
expect(head.style).to.respondTo("toComponent");
|
|
|
|
const styleComponent = head.style.toComponent();
|
|
|
|
expect(styleComponent).to.be.an("array").that.has.length.of(2);
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{styleComponent}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<div>${stringifiedStyleTags}</div>`);
|
|
});
|
|
|
|
it("renders title tag as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet title={"Dangerous <script> include"} />,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
|
|
expect(head.title.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedTitle);
|
|
});
|
|
|
|
it("renders title with itemprop name as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
title={"Title with Itemprop"}
|
|
titleAttributes={{itemprop: "name"}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
|
|
const titleString = head.title.toString();
|
|
expect(titleString).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedTitleWithItemprop);
|
|
});
|
|
|
|
it("renders base tags as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet base={{target: "_blank", href: "http://localhost/"}} />,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.base).to.exist;
|
|
expect(head.base).to.respondTo("toString");
|
|
|
|
expect(head.base.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedBaseTag);
|
|
});
|
|
|
|
it("renders meta tags as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{charset: "utf-8"},
|
|
{
|
|
name: "description",
|
|
content:
|
|
"Test description & encoding of special characters like ' \" > < `"
|
|
},
|
|
{"http-equiv": "content-type", content: "text/html"},
|
|
{property: "og:type", content: "article"},
|
|
{itemprop: "name", content: "Test name itemprop"}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.meta).to.exist;
|
|
expect(head.meta).to.respondTo("toString");
|
|
|
|
expect(head.meta.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedMetaTags);
|
|
});
|
|
|
|
it("renders link tags as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{href: "http://localhost/helmet", rel: "canonical"},
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.link).to.exist;
|
|
expect(head.link).to.respondTo("toString");
|
|
|
|
expect(head.link.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedLinkTags);
|
|
});
|
|
|
|
it("renders script tags as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
},
|
|
{
|
|
src: "http://localhost/test2.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.script).to.exist;
|
|
expect(head.script).to.respondTo("toString");
|
|
|
|
expect(head.script.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedScriptTags);
|
|
});
|
|
|
|
it("renders style tags as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
style={[
|
|
{
|
|
type: "text/css",
|
|
cssText: `body {background-color: green;}`
|
|
},
|
|
{
|
|
type: "text/css",
|
|
cssText: `p {font-size: 12px;}`
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.style).to.exist;
|
|
expect(head.style).to.respondTo("toString");
|
|
|
|
expect(head.style.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedStyleTags);
|
|
});
|
|
|
|
it("renders html attributes as component", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "ga",
|
|
className: "myClassName"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const {htmlAttributes} = Helmet.rewind();
|
|
const attrs = htmlAttributes.toComponent();
|
|
|
|
expect(attrs).to.exist;
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<html lang="en" {...attrs} />
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(`<html ${stringifiedHtmlAttributes}></html>`);
|
|
});
|
|
|
|
it("renders html attributes as string", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
htmlAttributes={{
|
|
lang: "ga",
|
|
class: "myClassName"
|
|
}}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.htmlAttributes).to.exist;
|
|
expect(head.htmlAttributes).to.respondTo("toString");
|
|
|
|
expect(head.htmlAttributes.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedHtmlAttributes);
|
|
});
|
|
|
|
it("does not encode all characters with HTML character entity equivalents", () => {
|
|
const chineseTitle = "膣膗 鍆錌雔";
|
|
const stringifiedChineseTitle = `<title ${HELMET_ATTRIBUTE}="true">${chineseTitle}</title>`;
|
|
|
|
ReactDOM.render(
|
|
<div>
|
|
<Helmet title={chineseTitle} />
|
|
</div>,
|
|
container
|
|
);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
|
|
expect(head.title.toString()).to.be
|
|
.a("string")
|
|
.that.equals(stringifiedChineseTitle);
|
|
});
|
|
|
|
it("rewind() provides a fallback object for empty Helmet state", () => {
|
|
ReactDOM.render(<div />, container);
|
|
|
|
const head = Helmet.rewind();
|
|
|
|
expect(head.htmlAttributes).to.exist;
|
|
expect(head.htmlAttributes).to.respondTo("toString");
|
|
expect(head.htmlAttributes.toString()).to.equal("");
|
|
expect(head.htmlAttributes).to.respondTo("toComponent");
|
|
expect(head.htmlAttributes.toComponent()).to.be.an("object").that.is
|
|
.empty;
|
|
|
|
expect(head.title).to.exist;
|
|
expect(head.title).to.respondTo("toString");
|
|
expect(head.title.toString()).to.equal(
|
|
`<title ${HELMET_ATTRIBUTE}="true"></title>`
|
|
);
|
|
expect(head.title).to.respondTo("toComponent");
|
|
|
|
const markup = ReactServer.renderToStaticMarkup(
|
|
<div>
|
|
{head.title.toComponent()}
|
|
</div>
|
|
);
|
|
|
|
expect(markup).to.be
|
|
.a("string")
|
|
.that.equals(
|
|
`<div><title ${HELMET_ATTRIBUTE}="true"></title></div>`
|
|
);
|
|
|
|
expect(head.base).to.exist;
|
|
expect(head.base).to.respondTo("toString");
|
|
expect(head.base.toString()).to.equal("");
|
|
expect(head.base).to.respondTo("toComponent");
|
|
expect(head.base.toComponent()).to.be.an("array").that.is.empty;
|
|
|
|
expect(head.meta).to.exist;
|
|
expect(head.meta).to.respondTo("toString");
|
|
expect(head.meta.toString()).to.equal("");
|
|
expect(head.meta).to.respondTo("toComponent");
|
|
expect(head.meta.toComponent()).to.be.an("array").that.is.empty;
|
|
|
|
expect(head.link).to.exist;
|
|
expect(head.link).to.respondTo("toString");
|
|
expect(head.link.toString()).to.equal("");
|
|
expect(head.link).to.respondTo("toComponent");
|
|
expect(head.link.toComponent()).to.be.an("array").that.is.empty;
|
|
|
|
expect(head.script).to.exist;
|
|
expect(head.script).to.respondTo("toString");
|
|
expect(head.script.toString()).to.equal("");
|
|
expect(head.script).to.respondTo("toComponent");
|
|
expect(head.script.toComponent()).to.be.an("array").that.is.empty;
|
|
|
|
expect(head.noscript).to.exist;
|
|
expect(head.noscript).to.respondTo("toString");
|
|
expect(head.noscript.toString()).to.equal("");
|
|
expect(head.noscript).to.respondTo("toComponent");
|
|
expect(head.noscript.toComponent()).to.be.an("array").that.is.empty;
|
|
|
|
expect(head.style).to.exist;
|
|
expect(head.style).to.respondTo("toString");
|
|
expect(head.style.toString()).to.equal("");
|
|
expect(head.style).to.respondTo("toComponent");
|
|
expect(head.style.toComponent()).to.be.an("array").that.is.empty;
|
|
});
|
|
|
|
it("does not render undefined attribute values", () => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "foo.js",
|
|
async: undefined
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
const {script} = Helmet.rewind();
|
|
const stringifiedScriptTag = script.toString();
|
|
|
|
expect(stringifiedScriptTag).to.be
|
|
.a("string")
|
|
.that.equals(
|
|
`<script ${HELMET_ATTRIBUTE}="true" src="foo.js" async></script>`
|
|
);
|
|
});
|
|
after(() => {
|
|
Helmet.canUseDOM = true;
|
|
});
|
|
});
|
|
|
|
describe("misc", () => {
|
|
it("throws in rewind() when a DOM is present", () => {
|
|
ReactDOM.render(<Helmet title={"Fancy title"} />, container);
|
|
|
|
expect(Helmet.rewind).to.throw(
|
|
"You may only call rewind() on the server. Call peek() to read the current state."
|
|
);
|
|
});
|
|
|
|
it("lets you read current state in peek() whether or not a DOM is present", done => {
|
|
ReactDOM.render(<Helmet title={"Fancy title"} />, container);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(Helmet.peek().title).to.be.equal("Fancy title");
|
|
Helmet.canUseDOM = false;
|
|
expect(Helmet.peek().title).to.be.equal("Fancy title");
|
|
Helmet.canUseDOM = true;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("encodes special characters", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{
|
|
name: "description",
|
|
content: 'This is "quoted" text and & and \'.'
|
|
}
|
|
]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(existingTag).to.have.property("getAttribute");
|
|
expect(existingTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(existingTag.getAttribute("content")).to.equal(
|
|
'This is "quoted" text and & and \'.'
|
|
);
|
|
expect(existingTag.outerHTML).to.equal(
|
|
`<meta name="description" content="This is "quoted" text and & and '." ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("does not change the DOM if it recevies identical props", done => {
|
|
const spy = sinon.spy();
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[{name: "description", content: "Test description"}]}
|
|
title={"Test Title"}
|
|
onChangeClientState={spy}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
// Re-rendering will pass new props to an already mounted Helmet
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[
|
|
{name: "description", content: "Test description"}
|
|
]}
|
|
title={"Test Title"}
|
|
onChangeClientState={spy}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.callCount).to.equal(1);
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("does not write the DOM if the client and server are identical", done => {
|
|
headElement.innerHTML = `<script ${HELMET_ATTRIBUTE}="true" src="http://localhost/test.js" type="text/javascript" />`;
|
|
|
|
const spy = sinon.spy();
|
|
ReactDOM.render(
|
|
<Helmet
|
|
script={[
|
|
{
|
|
src: "http://localhost/test.js",
|
|
type: "text/javascript"
|
|
}
|
|
]}
|
|
onChangeClientState={spy}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.called).to.equal(true);
|
|
|
|
const [, addedTags, removedTags] = spy.getCall(0).args;
|
|
|
|
expect(addedTags).to.be.empty;
|
|
expect(removedTags).to.be.empty;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("only adds new tags and preserves tags when rendering additional Helmet instances", done => {
|
|
const spy = sinon.spy();
|
|
let addedTags;
|
|
let removedTags;
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
}
|
|
]}
|
|
meta={[{name: "description", content: "Test description"}]}
|
|
onChangeClientState={spy}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.called).to.equal(true);
|
|
addedTags = spy.getCall(0).args[1];
|
|
removedTags = spy.getCall(0).args[2];
|
|
|
|
expect(addedTags).to.have.property("metaTags");
|
|
expect(addedTags.metaTags).to.have.deep.property("[0]");
|
|
expect(addedTags.metaTags[0].outerHTML).to.equal(
|
|
`<meta name="description" content="Test description" data-react-helmet="true">`
|
|
);
|
|
expect(addedTags).to.have.property("linkTags");
|
|
expect(addedTags.linkTags).to.have.deep.property("[0]");
|
|
expect(addedTags.linkTags[0].outerHTML).to.equal(
|
|
`<link href="http://localhost/style.css" rel="stylesheet" type="text/css" data-react-helmet="true">`
|
|
);
|
|
expect(removedTags).to.be.empty;
|
|
|
|
// Re-rendering will pass new props to an already mounted Helmet
|
|
ReactDOM.render(
|
|
<Helmet
|
|
link={[
|
|
{
|
|
href: "http://localhost/style.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
},
|
|
{
|
|
href: "http://localhost/style2.css",
|
|
rel: "stylesheet",
|
|
type: "text/css"
|
|
}
|
|
]}
|
|
meta={[
|
|
{name: "description", content: "New description"}
|
|
]}
|
|
onChangeClientState={spy}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(spy.callCount).to.equal(2);
|
|
addedTags = spy.getCall(1).args[1];
|
|
removedTags = spy.getCall(1).args[2];
|
|
|
|
expect(addedTags).to.have.property("metaTags");
|
|
expect(addedTags.metaTags).to.have.deep.property("[0]");
|
|
expect(addedTags.metaTags[0].outerHTML).to.equal(
|
|
`<meta name="description" content="New description" data-react-helmet="true">`
|
|
);
|
|
expect(addedTags).to.have.property("linkTags");
|
|
expect(addedTags.linkTags).to.have.deep.property("[0]");
|
|
expect(addedTags.linkTags[0].outerHTML).to.equal(
|
|
`<link href="http://localhost/style2.css" rel="stylesheet" type="text/css" data-react-helmet="true">`
|
|
);
|
|
expect(removedTags).to.have.property("metaTags");
|
|
expect(removedTags.metaTags).to.have.deep.property("[0]");
|
|
expect(removedTags.metaTags[0].outerHTML).to.equal(
|
|
`<meta name="description" content="Test description" data-react-helmet="true">`
|
|
);
|
|
expect(removedTags).to.not.have.property("linkTags");
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("does not accept nested Helmets", done => {
|
|
const warn = sinon.stub(console, "warn");
|
|
|
|
ReactDOM.render(
|
|
<Helmet title={"Test Title"}>
|
|
<Helmet title={"Title you'll never see"} />
|
|
</Helmet>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
expect(document.title).to.equal("Test Title");
|
|
expect(warn.called).to.be.true;
|
|
|
|
const [warning] = warn.getCall(0).args;
|
|
expect(warning).to.equal(
|
|
"You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information."
|
|
);
|
|
|
|
warn.restore();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("recognizes valid tags regardless of attribute ordering", done => {
|
|
ReactDOM.render(
|
|
<Helmet
|
|
meta={[{content: "Test Description", name: "description"}]}
|
|
/>,
|
|
container
|
|
);
|
|
|
|
requestAnimationFrame(() => {
|
|
const existingTags = headElement.querySelectorAll(
|
|
`meta[${HELMET_ATTRIBUTE}]`
|
|
);
|
|
const existingTag = existingTags[0];
|
|
|
|
expect(existingTags).to.not.equal(undefined);
|
|
|
|
expect(existingTags.length).to.be.equal(1);
|
|
|
|
expect(existingTags).to.have.deep
|
|
.property("[0]")
|
|
.that.is.an.instanceof(Element);
|
|
expect(existingTag).to.have.property("getAttribute");
|
|
expect(existingTag.getAttribute("name")).to.equal(
|
|
"description"
|
|
);
|
|
expect(existingTag.getAttribute("content")).to.equal(
|
|
"Test Description"
|
|
);
|
|
expect(existingTag.outerHTML).to.equal(
|
|
`<meta content="Test Description" name="description" ${HELMET_ATTRIBUTE}="true">`
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("requestAnimationFrame works as expected", done => {
|
|
requestAnimationFrame(cb => {
|
|
expect(cb).to.exist;
|
|
expect(cb).to.be.a("number");
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|