import { parse } from 'node-html-parser'
import { checkPngFavicon, checkSvgFavicon } from "./desktop";
import { CheckerMessage, CheckerStatus, DesktopFaviconReport, FetchResponse, MessageId } from '../types';
import { filePathToDataUrl, filePathToReadableStream, filePathToString, stringToReadableStream } from '../helper';
import { testFetcher } from '../test-helper';
type TestOutput = {
messages: Pick[];
icons: DesktopFaviconReport['icons'];
}
const runSvgTest = async (
headFragment: string | null,
output: TestOutput,
fetchDatabase: { [url: string]: FetchResponse } = {}
) => {
const root = headFragment ? parse(headFragment) : null;
const result = await checkSvgFavicon('https://example.com/', root, testFetcher(fetchDatabase));
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
expect(filteredMessages).toEqual(output.messages);
}
test('checkSvgFavicon - noHead', async () => {
await runSvgTest(null, {
messages: [{
status: CheckerStatus.Error,
id: MessageId.noHead,
}],
icons: {
png: null,
ico: null,
svg: null,
}
});
})
test('checkSvgFavicon - noSvgFavicon', async () => {
await runSvgTest(`Some text`, {
messages: [{
status: CheckerStatus.Error,
id: MessageId.noSvgFavicon,
}],
icons: {
png: null,
ico: null,
svg: null,
}
});
})
test('checkSvgFavicon - multipleSvgFavicons', async () => {
await runSvgTest(`
`, {
messages: [{
status: CheckerStatus.Error,
id: MessageId.multipleSvgFavicons,
}], icons: {
png: null,
ico: null,
svg: null,
}
});
})
test('checkSvgFavicon - svgFaviconDeclared & noSvgFaviconHref', async () => {
await runSvgTest(``, {
messages: [{
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDeclared,
}, {
status: CheckerStatus.Error,
id: MessageId.noSvgFaviconHref,
}],
icons: {
png: null,
ico: null,
svg: null,
}
});
})
test('checkSvgFavicon - svgFaviconDeclared & svgFavicon404', async () => {
await runSvgTest(``, {
messages: [{
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDeclared,
}, {
status: CheckerStatus.Error,
id: MessageId.svgFavicon404,
}],
icons: {
png: null,
ico: null,
svg: {
content: null,
url: 'https://example.com/the-icon.svg',
width: null,
height: null,
},
}
});
})
test('checkSvgFavicon - svgFaviconDeclared & svgFaviconCannotGet', async () => {
await runSvgTest(``, {
messages: [{
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDeclared,
}, {
status: CheckerStatus.Error,
id: MessageId.svgFaviconCannotGet,
}],
icons: {
png: null,
ico: null,
svg: {
content: null,
url: 'https://example.com/the-icon.svg',
width: null,
height: null,
},
}
}, {
'https://example.com/the-icon.svg': {
status: 403,
contentType: 'image/svg+xml'
}
});
})
// For https://github.com/RealFaviconGenerator/core/issues/2
test('checkSvgFavicon - Protocol-relative URL', async () => {
await runSvgTest(``, {
messages: [{
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDeclared,
}, {
status: CheckerStatus.Error,
id: MessageId.svgFaviconCannotGet,
}], icons: {
png: null,
ico: null,
svg: {
content: null,
url: 'https://example.com/the-icon.svg',
width: null,
height: null,
},
}
}, {
'https://example.com/the-icon.svg': {
status: 403,
contentType: 'image/svg+xml'
}
});
})
test('checkSvgFavicon - svgFaviconDeclared & svgFaviconDownloadable & svgFaviconSquare', async () => {
const testIconPath = './fixtures/happy-face.svg';
const serpIcon = await filePathToString(testIconPath);
await runSvgTest(``, {
messages: [
{
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDeclared,
}, {
status: CheckerStatus.Ok,
id: MessageId.svgFaviconDownloadable,
}, {
status: CheckerStatus.Ok,
id: MessageId.svgFaviconSquare,
}],
icons: {
png: null,
ico: null,
svg: {
content: await filePathToDataUrl(testIconPath),
url: 'https://example.com/the-icon.svg',
width: 36,
height: 36,
},
}
}, {
'https://example.com/the-icon.svg': {
status: 200,
contentType: 'image/svg+xml',
readableStream: await filePathToReadableStream(testIconPath)
}
});
})
const runPngTest = async (
headFragment: string | null,
output: TestOutput,
fetchDatabase: { [url: string]: FetchResponse } = {}
) => {
const root = headFragment ? parse(headFragment) : null;
const result = await checkPngFavicon('https://example.com/', root, testFetcher(fetchDatabase));
const filteredMessages = result.messages.map(m => ({ status: m.status, id: m.id }));
expect(filteredMessages).toEqual(output.messages);
}
const testIcon16 = './fixtures/16x16.png';
const testIcon32 = './fixtures/32x32.png';
const testIcon48 = './fixtures/48x48.png';
const testIcon96 = './fixtures/96x96.png';
test('checkSvgFavicon - Three PNG icons with different sizes', async () => {
await runPngTest(`
`, { messages: [{
status: CheckerStatus.Ok,
id: MessageId.desktopPngFaviconDeclared,
}, {
status: CheckerStatus.Ok,
id: MessageId.desktopPngFaviconDownloadable,
}, {
status: CheckerStatus.Ok,
id: MessageId.desktopPngFaviconRightSize,
}],
icons: {
png: {
content: await filePathToDataUrl(testIcon96),
url: 'https://example.com/favicon/favicon-96x96.png',
width: 96,
height: 96,
},
ico: null,
svg: null,
}
},
{
'https://example.com/favicon/favicon-16x16.png': {
status: 200,
contentType: 'image/png',
readableStream: await filePathToReadableStream(testIcon16),
},
'https://example.com/favicon/favicon-32x32.png': {
status: 200,
contentType: 'image/png',
readableStream: await filePathToReadableStream(testIcon32),
},
'https://example.com/favicon/favicon-48x48.png': {
status: 200,
contentType: 'image/png',
readableStream: await filePathToReadableStream(testIcon48),
},
'https://example.com/favicon/favicon-96x96.png': {
status: 200,
contentType: 'image/png',
readableStream: await filePathToReadableStream(testIcon96),
}
});
})