inlineresources
Version:
Inlines style sheets, images, fonts and scripts in HTML documents. Works in the browser.
308 lines (232 loc) • 11.8 kB
JavaScript
;
var inline = require('../../src/inline'),
inlineCss = require('../../src/inlineCss'),
util = require('../../src/util'),
testHelper = require('../testHelper');
describe("Inline styles", function () {
var doc, loadCSSImportsForRulesSpy, loadAndInlineCSSResourcesForRulesSpy;
var fulfilled = function (value) {
return Promise.resolve(value);
};
beforeEach(function () {
doc = document.implementation.createHTMLDocument("");
loadCSSImportsForRulesSpy = spyOn(inlineCss, 'loadCSSImportsForRules').and.returnValue(fulfilled({
hasChanges: false,
errors: []
}));
loadAndInlineCSSResourcesForRulesSpy = spyOn(inlineCss, 'loadAndInlineCSSResourcesForRules').and.returnValue(fulfilled({
hasChanges: false,
errors: []
}));
spyOn(util, 'clone').and.callFake(function (object) {
return object;
});
});
it("should do nothing if no CSS is found", function (done) {
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).not.toHaveBeenCalled();
done();
});
});
it("should not touch unrelated CSS", function (done) {
testHelper.addStyleToDocument(doc, "span { padding-left: 0; }");
loadCSSImportsForRulesSpy.and.callFake(function(rules) {
rules[0] = "fake rule";
return fulfilled({
hasChanges: false,
errors: []
});
});
loadAndInlineCSSResourcesForRulesSpy.and.callFake(function(rules) {
rules[0] = "something else";
return fulfilled({
hasChanges: false,
errors: []
});
});
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(doc.head.getElementsByTagName("style")[0].textContent).toEqual("span { padding-left: 0; }");
done();
});
});
it("should replace an import with the content of the given URL", function (done) {
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
expect(loadCSSImportsForRulesSpy.calls.mostRecent().args[0][0].cssText).toMatch(/@import url\([ "]?that.css[ "]?\)\s*;/);
done();
});
});
it("should inline css resources", function (done) {
testHelper.addStyleToDocument(doc, 'span { background-image: url("anImage.png"); }');
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy.calls.mostRecent().args[0][0].cssText).toMatch(/span \{\s*background-image: url\("?anImage.png"?\)\s*;\s*\}/);
done();
});
});
it("should accept a style element without a type", function (done) {
var styleNode = doc.createElement("style");
styleNode.appendChild(doc.createTextNode('@import url("imported.css");'));
doc.head.appendChild(styleNode);
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalled();
done();
});
});
it("should ignore a style element with a non CSS type", function (done) {
var styleNode = doc.createElement("style");
styleNode.type = "text/plain";
styleNode.appendChild(doc.createTextNode('@import url("imported.css");'));
doc.head.appendChild(styleNode);
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).not.toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy).not.toHaveBeenCalled();
done();
});
});
it("should respect the document's baseURI", function (done) {
var getDocumentBaseUrlSpy = spyOn(util, 'getDocumentBaseUrl').and.callThrough();
doc = testHelper.readDocumentFixture("importCss.html");
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalledWith(jasmine.any(Object), [], {baseUrl: doc.baseURI});
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalledWith(jasmine.any(Object), {baseUrl: doc.baseURI});
expect(getDocumentBaseUrlSpy).toHaveBeenCalledWith(doc);
done();
});
});
it("should favour explicit baseUrl over document.baseURI", function (done) {
var baseUrl = "aBaseURI";
doc = testHelper.readDocumentFixture("importCss.html");
expect(doc.baseURI).not.toBeNull();
expect(doc.baseURI).not.toEqual("about:blank");
expect(doc.baseURI).not.toEqual(baseUrl);
inline.loadAndInlineStyles(doc, {baseUrl: baseUrl}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalledWith(jasmine.any(Object), [], {baseUrl: baseUrl});
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalledWith(jasmine.any(Object), {baseUrl: baseUrl});
done();
});
});
it("should circumvent caching if requested", function (done) {
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {cache: 'none'}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
expect(loadCSSImportsForRulesSpy.calls.mostRecent().args[2].cache).toEqual('none');
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy.calls.mostRecent().args[1].cache).toEqual('none');
done();
});
});
it("should not circumvent caching by default", function (done) {
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
expect(loadCSSImportsForRulesSpy.calls.mostRecent().args[2]).toBeTruthy();
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy.calls.mostRecent().args[1].cache).not.toBe(false);
done();
});
});
it("should cache inlined content if a cache bucket is given", function (done) {
var cacheBucket = {};
loadAndInlineCSSResourcesForRulesSpy.and.returnValue(fulfilled({
hasChanges: true,
errors: []
}));
// first call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
loadCSSImportsForRulesSpy.calls.reset();
loadAndInlineCSSResourcesForRulesSpy.calls.reset();
// second call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function () {
expect(loadCSSImportsForRulesSpy).not.toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy).not.toHaveBeenCalled();
expect(doc.getElementsByTagName("style")[0].textContent).toMatch(/background-image\s*{\s*}/);
done();
});
});
});
it("should not use cache inlined content if the documents' URLs don't match", function (done) {
var cacheBucket = {};
loadAndInlineCSSResourcesForRulesSpy.and.returnValue(fulfilled({
hasChanges: true,
errors: []
}));
// first call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
loadCSSImportsForRulesSpy.calls.reset();
loadAndInlineCSSResourcesForRulesSpy.calls.reset();
// second call
testHelper.loadHTMLDocumentFixture("image.html").then(function (doc) { // use a document with different baseUrl
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
expect(loadAndInlineCSSResourcesForRulesSpy).toHaveBeenCalled();
done();
});
});
});
});
it("should not cache inlined content if caching turned off", function (done) {
var cacheBucket = {};
// first call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket, cache: 'none'}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
loadCSSImportsForRulesSpy.calls.reset();
// second call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, 'background-image { url(anImage.png); }');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket, cache: 'none'}).then(function () {
expect(loadCSSImportsForRulesSpy).toHaveBeenCalled();
done();
});
});
});
describe("error handling", function () {
it("should report errors", function (done) {
loadCSSImportsForRulesSpy.and.returnValue(fulfilled({
hasChanges: false,
errors: ['import error']
}));
loadAndInlineCSSResourcesForRulesSpy.and.returnValue(fulfilled({
hasChanges: false,
errors: ['resource error']
}));
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {}).then(function (errors) {
expect(errors).toEqual(['import error', 'resource error']);
done();
});
});
it("should cache errors alongside if a cache bucket is given", function (done) {
var cacheBucket = {};
loadCSSImportsForRulesSpy.and.returnValue(fulfilled({
hasChanges: false,
errors: ['import error']
}));
// first call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function () {
// second call
doc = document.implementation.createHTMLDocument("");
testHelper.addStyleToDocument(doc, '@import url("that.css");');
inline.loadAndInlineStyles(doc, {cacheBucket: cacheBucket}).then(function (errors) {
expect(errors).toEqual(["import error"]);
done();
});
});
});
});
});