mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 10:21:36 +03:00
6161f94910
refs: https://github.com/TryGhost/Toolbox/issues/595 We're rolling out new rules around the node assert library, the first of which is enforcing the use of assert/strict. This means we don't need to use the strict version of methods, as the standard version will work that way by default. This caught some gotchas in our existing usage of assert where the lack of strict mode had unexpected results: - Url matching needs to be done on `url.href` seeaa58b354a4
- Null and undefined are not the same thing, there were a few cases of this being confused - Particularly questionable changes in [PostExporter tests](c1a468744b
) tracked [here](https://github.com/TryGhost/Team/issues/3505). - A typo seeeaac9c293a
Moving forward, using assert strict should help us to catch unexpected behaviour, particularly around nulls and undefineds during implementation.
530 lines
22 KiB
JavaScript
530 lines
22 KiB
JavaScript
const assert = require('assert/strict');
|
|
const sinon = require('sinon');
|
|
const nock = require('nock');
|
|
const path = require('path');
|
|
const loggingLib = require('@tryghost/logging');
|
|
const ExternalMediaInliner = require('../index');
|
|
|
|
describe('ExternalMediaInliner', function () {
|
|
let logging;
|
|
let GIF1x1;
|
|
let postModelStub;
|
|
let postMetaModelStub;
|
|
let tagModelStub;
|
|
let userModelStub;
|
|
|
|
beforeEach(function () {
|
|
// use a 1x1 gif in nock responses because it's really small and easy to work with
|
|
GIF1x1 = Buffer.from('R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'base64');
|
|
logging = {
|
|
info: sinon.stub(loggingLib, 'info'),
|
|
error: sinon.stub(loggingLib, 'error'),
|
|
warn: sinon.stub(loggingLib, 'warn')
|
|
};
|
|
|
|
postModelStub = {
|
|
findPage: sinon.stub().resolves({
|
|
data: []
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
postMetaModelStub = {
|
|
findPage: sinon.stub().resolves({
|
|
data: []
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
tagModelStub = {
|
|
findPage: sinon.stub().resolves({
|
|
data: []
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
userModelStub = {
|
|
findPage: sinon.stub().resolves({
|
|
data: []
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
});
|
|
|
|
afterEach(function () {
|
|
sinon.restore();
|
|
nock.cleanAll();
|
|
});
|
|
|
|
it('Creates an External Media Inliner instance', function () {
|
|
assert.ok(new ExternalMediaInliner({}));
|
|
});
|
|
|
|
describe('inline', function () {
|
|
it('inlines image in the post\'s mobiledoc content', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
const postModelInstanceStub = {
|
|
id: 'inlined-post-id',
|
|
get: sinon.stub()
|
|
.withArgs('mobiledoc')
|
|
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`)
|
|
};
|
|
postModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelInstanceStub]
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-image.jpg')
|
|
.returns('unique-image.jpg');
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-image.jpg',
|
|
saveRaw: () => '/content/images/unique-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(postModelStub.edit.calledOnce);
|
|
assert.ok(postModelStub.edit.calledWith({
|
|
mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"__GHOST_URL__/content/images/unique-image.jpg"}]]}'
|
|
}, {
|
|
id: 'inlined-post-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
}));
|
|
});
|
|
|
|
it('inlines the image from post\'s mobiledoc containing html card', async function () {
|
|
const imageURL = 'https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860';
|
|
const requestMock = nock('https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com')
|
|
.get('/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860')
|
|
.reply(200, GIF1x1);
|
|
|
|
const postModelInstanceStub = {
|
|
id: 'inlined-post-with-htmlcard-id',
|
|
get: sinon.stub()
|
|
.withArgs('mobiledoc')
|
|
.returns(`{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"<img src="${imageURL}" alt="Lorem ipsum">"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}`)
|
|
};
|
|
|
|
postModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelInstanceStub]
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-image.jpg')
|
|
.returns('unique-image.jpg');
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-image.jpg',
|
|
saveRaw: () => '/content/images/unique-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(postModelStub.edit.calledOnce);
|
|
assert.deepEqual(postModelStub.edit.args[0][0], {
|
|
mobiledoc: `{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"<img src="__GHOST_URL__/content/images/unique-image.jpg" alt="Lorem ipsum">"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}`
|
|
});
|
|
assert.deepEqual(postModelStub.edit.args[0][1], {
|
|
id: 'inlined-post-with-htmlcard-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
});
|
|
});
|
|
|
|
it('logs an error when fetching an external media fails', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/image.jpg')
|
|
.reply(404);
|
|
const postModelInstanceStub = {
|
|
id: 'inlined-post-id',
|
|
get: sinon.stub()
|
|
.withArgs('mobiledoc')
|
|
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`)
|
|
};
|
|
|
|
postModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelInstanceStub]
|
|
})
|
|
};
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.equal(logging.error.args[0][0], 'Error downloading remote media: https://img.stockfresh.com/files/f/image.jpg');
|
|
});
|
|
|
|
it('logs an error when fetching an external media for simple fields fails', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/simple-image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/simple-image.jpg')
|
|
.reply(500);
|
|
const userModelInstanceStub = {
|
|
id: 'inlined-user-id',
|
|
get: sinon.stub()
|
|
.withArgs('profile_image')
|
|
.returns(imageURL)
|
|
};
|
|
|
|
userModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [userModelInstanceStub]
|
|
})
|
|
};
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.equal(logging.error.args[0][0], 'Error downloading remote media: https://img.stockfresh.com/files/f/simple-image.jpg');
|
|
});
|
|
|
|
it('logs a warning when no suitable storage adapter found for inlined media extension', async function () {
|
|
const fileURL = 'https://img.stockfresh.com/files/f/inlined.exe';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/inlined.exe')
|
|
.reply(200, GIF1x1);
|
|
|
|
const postModelInstanceStub = {
|
|
id: 'inlined-post-id',
|
|
get: sinon.stub()
|
|
.withArgs('mobiledoc')
|
|
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${fileURL}"}]]}`)
|
|
};
|
|
postModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelInstanceStub]
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.exe').returns(null)
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(logging.warn.calledOnce);
|
|
assert.equal(logging.warn.args[0][0], 'No storage adapter found for file extension: .exe');
|
|
});
|
|
|
|
it('logs an error when handling post inlining throws an error', async function (){
|
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
postModelStub = {
|
|
id: 'errored-post-id',
|
|
get: sinon.stub()
|
|
.withArgs('mobiledoc')
|
|
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`)
|
|
};
|
|
postModelStub = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelStub]
|
|
}),
|
|
edit: sinon.stub().throws(new Error('Error saving the post'))
|
|
};
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-image.jpg')
|
|
.returns('unique-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-image.jpg',
|
|
saveRaw: () => '/content/images/unique-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(postModelStub.edit.calledOnce);
|
|
assert.equal(logging.error.args[0][0], 'Error inlining media for post: errored-post-id');
|
|
});
|
|
|
|
it('logs an error when handling tag simple fields inlining throws an error', async function (){
|
|
const imageURL = 'https://img.stockfresh.com/files/f/simple-image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/simple-image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
const getMethodStub = sinon.stub();
|
|
getMethodStub.withArgs('feature_image').returns(imageURL);
|
|
getMethodStub.withArgs('og_image').returns(null);
|
|
getMethodStub.withArgs('twitter_image').returns(null);
|
|
|
|
const tagModelInstanceStub = {
|
|
id: 'errored-tag-id',
|
|
get: getMethodStub
|
|
};
|
|
tagModelStub.findPage = sinon.stub().returns({
|
|
data: [tagModelInstanceStub]
|
|
});
|
|
tagModelStub.edit = sinon.stub().throws(new Error('Error saving the tag'));
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-image.jpg')
|
|
.returns('unique-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-image.jpg',
|
|
saveRaw: () => '/content/images/unique-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(tagModelStub.edit.calledOnce);
|
|
assert.equal(logging.error.args[0][0], 'Error inlining media for: errored-tag-id');
|
|
});
|
|
|
|
it('inlines image in the post\'s feature_image field', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/posts_feature_image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/posts_feature_image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
postModelStub = {
|
|
id: 'inlined-post-id',
|
|
get: sinon.stub()
|
|
.withArgs('feature_image')
|
|
.returns(imageURL)
|
|
};
|
|
const postModelMock = {
|
|
findPage: sinon.stub().returns({
|
|
data: [postModelStub]
|
|
}),
|
|
edit: sinon.stub().resolves()
|
|
};
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-feature-image.jpg')
|
|
.returns('unique-feature-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelMock,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-feature-image.jpg',
|
|
saveRaw: () => '/content/images/unique-feature-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(postModelMock.edit.calledOnce);
|
|
assert.ok(postModelMock.edit.calledWith({
|
|
feature_image: '__GHOST_URL__/content/images/unique-feature-image.jpg'
|
|
}, {
|
|
id: 'inlined-post-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
}));
|
|
});
|
|
|
|
it('inlines og_image image in posts_meta table', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/posts_meta_image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/posts_meta_image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
const getMethodStub = sinon.stub();
|
|
getMethodStub.withArgs('og_image').returns(imageURL);
|
|
getMethodStub.withArgs('twitter_image').returns(null);
|
|
const postsMetaModelInstanceStub = {
|
|
id: 'inlined-post-meta-id',
|
|
get: getMethodStub
|
|
};
|
|
|
|
postMetaModelStub.findPage = sinon.stub().resolves({
|
|
data: [postsMetaModelInstanceStub]
|
|
});
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-feature-image.jpg')
|
|
.returns('unique-feature-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-posts-meta-image.jpg',
|
|
saveRaw: () => '/content/images/unique-posts-meta-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(postMetaModelStub.edit.calledOnce);
|
|
assert.deepEqual(postMetaModelStub.edit.args[0][0], {
|
|
og_image: '__GHOST_URL__/content/images/unique-posts-meta-image.jpg'
|
|
});
|
|
assert.deepEqual(postMetaModelStub.edit.args[0][1], {
|
|
id: 'inlined-post-meta-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
});
|
|
});
|
|
|
|
it('inlines twitter_image image in tags table', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/tag_twitter_image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/tag_twitter_image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
const getMethodStub = sinon.stub();
|
|
getMethodStub.withArgs('twitter_image').returns(imageURL);
|
|
getMethodStub.returns(null);
|
|
const tagModelInstanceStub = {
|
|
id: 'inlined-tag-id',
|
|
get: getMethodStub
|
|
};
|
|
|
|
tagModelStub.findPage = sinon.stub().resolves({
|
|
data: [tagModelInstanceStub]
|
|
});
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/unique-tag-twitter-image.jpg')
|
|
.returns('unique-tag-twitter-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/unique-tag-twitter-image.jpg',
|
|
saveRaw: () => '/content/images/unique-tag-twitter-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(tagModelStub.edit.calledOnce);
|
|
assert.deepEqual(tagModelStub.edit.args[0][0], {
|
|
twitter_image: '__GHOST_URL__/content/images/unique-tag-twitter-image.jpg'
|
|
});
|
|
assert.deepEqual(tagModelStub.edit.args[0][1], {
|
|
id: 'inlined-tag-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
});
|
|
});
|
|
|
|
it('inlines cover_image image in users table', async function () {
|
|
const imageURL = 'https://img.stockfresh.com/files/f/user_cover_image.jpg';
|
|
const requestMock = nock('https://img.stockfresh.com')
|
|
.get('/files/f/user_cover_image.jpg')
|
|
.reply(200, GIF1x1);
|
|
|
|
const getMethodStub = sinon.stub();
|
|
getMethodStub.withArgs('cover_image').returns(imageURL);
|
|
getMethodStub.returns(null);
|
|
const userModelInstanceStub = {
|
|
id: 'inlined-user-id',
|
|
get: getMethodStub
|
|
};
|
|
|
|
userModelStub.findPage = sinon.stub().resolves({
|
|
data: [userModelInstanceStub]
|
|
});
|
|
sinon.stub(path, 'relative')
|
|
.withArgs('/content/images', '/content/images/user-cover-image.jpg')
|
|
.returns('user-cover-image.jpg');
|
|
|
|
const inliner = new ExternalMediaInliner({
|
|
PostModel: postModelStub,
|
|
PostMetaModel: postMetaModelStub,
|
|
TagModel: tagModelStub,
|
|
UserModel: userModelStub,
|
|
getMediaStorage: sinon.stub().withArgs('.jpg').returns({
|
|
getTargetDir: () => '/content/images',
|
|
getUniqueFileName: () => '/content/images/user-cover-image.jpg',
|
|
saveRaw: () => '/content/images/user-cover-image.jpg'
|
|
})
|
|
});
|
|
|
|
await inliner.inline(['https://img.stockfresh.com']);
|
|
|
|
assert.ok(requestMock.isDone());
|
|
assert.ok(userModelStub.edit.calledOnce);
|
|
assert.deepEqual(userModelStub.edit.args[0][0], {
|
|
cover_image: '__GHOST_URL__/content/images/user-cover-image.jpg'
|
|
});
|
|
assert.deepEqual(userModelStub.edit.args[0][1], {
|
|
id: 'inlined-user-id',
|
|
context: {
|
|
internal: true
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|