From 0eb730f1baed38f8adcd1587fb9fe7805791bdef Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 7 Dec 2020 15:14:41 +1100 Subject: [PATCH] fix lint --- test/models/conversations_test.js | 315 +-- .../linkPreviews/linkPreviewFetch_test.ts | 2436 ++++++++--------- 2 files changed, 1299 insertions(+), 1452 deletions(-) diff --git a/test/models/conversations_test.js b/test/models/conversations_test.js index 9769718d6..ab05f7fb8 100644 --- a/test/models/conversations_test.js +++ b/test/models/conversations_test.js @@ -2,170 +2,151 @@ // 'use strict'; // FIXME audric enable back those test -// describe('ConversationCollection', () => { -// textsecure.messaging = new textsecure.MessageSender(); - -// before(clearDatabase); -// after(clearDatabase); - -// it('should be ordered newest to oldest', () => { -// const conversations = new Whisper.ConversationCollection(); -// // Timestamps -// const today = new Date(); -// const tomorrow = new Date(); -// tomorrow.setDate(today.getDate() + 1); - -// // Add convos -// conversations.add({ timestamp: today }); -// conversations.add({ timestamp: tomorrow }); - -// const { models } = conversations; -// const firstTimestamp = models[0].get('timestamp').getTime(); -// const secondTimestamp = models[1].get('timestamp').getTime(); - -// // Compare timestamps -// assert(firstTimestamp > secondTimestamp); -// }); -// }); - -// describe('Conversation', () => { -// const attributes = { -// type: 'private', -// id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }; -// before(async () => { -// const convo = new Whisper.ConversationCollection().add(attributes); -// await window.Signal.Data.saveConversation(convo.attributes, { -// Conversation: Whisper.Conversation, -// }); - -// // const message = convo.messageCollection.add({ -// // body: 'hello world', -// // conversationId: convo.id, -// // type: 'outgoing', -// // sent_at: Date.now(), -// // received_at: Date.now(), -// // }); -// // await message.commit(false); -// }); -// after(clearDatabase); - -// it('sorts its contacts in an intl-friendly way', () => { -// const convo = new Whisper.Conversation({ -// id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// convo.contactCollection.add( -// new Whisper.Conversation({ -// name: 'C', -// }) -// ); -// convo.contactCollection.add( -// new Whisper.Conversation({ -// name: 'B', -// }) -// ); -// convo.contactCollection.add( -// new Whisper.Conversation({ -// name: 'Á', -// }) -// ); - -// assert.strictEqual(convo.contactCollection.at('0').get('name'), 'Á'); -// assert.strictEqual(convo.contactCollection.at('1').get('name'), 'B'); -// assert.strictEqual(convo.contactCollection.at('2').get('name'), 'C'); -// }); - -// it('contains its own messages', async () => { -// const convo = new Whisper.ConversationCollection().add({ -// id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// await convo.fetchMessages(); -// assert.notEqual(convo.messageCollection.length, 0); -// }); - -// it('contains only its own messages', async () => { -// const convo = new Whisper.ConversationCollection().add({ -// id: '052d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// await convo.fetchMessages(); -// assert.strictEqual(convo.messageCollection.length, 0); -// }); - -// it('adds conversation to message collection upon leaving group', async () => { -// const convo = new Whisper.ConversationCollection().add({ -// type: 'group', -// id: '052d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// await convo.leaveGroup(); -// assert.notEqual(convo.messageCollection.length, 0); -// }); - -// it('has a title', () => { -// const convos = new Whisper.ConversationCollection(); -// let convo = convos.add(attributes); -// assert.equal( -// convo.getTitle(), -// '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab' -// ); - -// convo = convos.add({ type: '' }); -// assert.equal(convo.getTitle(), 'Unknown group'); - -// convo = convos.add({ name: 'name' }); -// assert.equal(convo.getTitle(), 'name'); -// }); - -// it('returns the number', () => { -// const convos = new Whisper.ConversationCollection(); -// let convo = convos.add(attributes); -// assert.equal( -// convo.getNumber(), -// '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab' -// ); - -// convo = convos.add({ type: '' }); -// assert.equal(convo.getNumber(), ''); -// }); - -// describe('when set to private', () => { -// it('correctly validates hex numbers', () => { -// const regularId = new Whisper.Conversation({ -// type: 'private', -// id: -// '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// const invalidId = new Whisper.Conversation({ -// type: 'private', -// id: -// 'j71d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// assert.ok(regularId.isValid()); -// assert.notOk(invalidId.isValid()); -// }); - -// it('correctly validates length', () => { -// const regularId33 = new Whisper.Conversation({ -// type: 'private', -// id: -// '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// const regularId32 = new Whisper.Conversation({ -// type: 'private', -// id: '1d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', -// }); -// const shortId = new Whisper.Conversation({ -// type: 'private', -// id: '771d11d', -// }); -// const longId = new Whisper.Conversation({ -// type: 'private', -// id: -// '771d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94abaa', -// }); -// assert.ok(regularId33.isValid()); -// assert.ok(regularId32.isValid()); -// assert.notOk(shortId.isValid()); -// assert.notOk(longId.isValid()); -// }); -// }); -// }); +describe('ConversationCollection', () => { + // textsecure.messaging = new textsecure.MessageSender(); + // before(clearDatabase); + // after(clearDatabase); + // it('should be ordered newest to oldest', () => { + // const conversations = new Whisper.ConversationCollection(); + // // Timestamps + // const today = new Date(); + // const tomorrow = new Date(); + // tomorrow.setDate(today.getDate() + 1); + // // Add convos + // conversations.add({ timestamp: today }); + // conversations.add({ timestamp: tomorrow }); + // const { models } = conversations; + // const firstTimestamp = models[0].get('timestamp').getTime(); + // const secondTimestamp = models[1].get('timestamp').getTime(); + // // Compare timestamps + // assert(firstTimestamp > secondTimestamp); + // }); + // }); + // describe('Conversation', () => { + // const attributes = { + // type: 'private', + // id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }; + // before(async () => { + // const convo = new Whisper.ConversationCollection().add(attributes); + // await window.Signal.Data.saveConversation(convo.attributes, { + // Conversation: Whisper.Conversation, + // }); + // // const message = convo.messageCollection.add({ + // // body: 'hello world', + // // conversationId: convo.id, + // // type: 'outgoing', + // // sent_at: Date.now(), + // // received_at: Date.now(), + // // }); + // // await message.commit(false); + // }); + // after(clearDatabase); + // it('sorts its contacts in an intl-friendly way', () => { + // const convo = new Whisper.Conversation({ + // id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // convo.contactCollection.add( + // new Whisper.Conversation({ + // name: 'C', + // }) + // ); + // convo.contactCollection.add( + // new Whisper.Conversation({ + // name: 'B', + // }) + // ); + // convo.contactCollection.add( + // new Whisper.Conversation({ + // name: 'Á', + // }) + // ); + // assert.strictEqual(convo.contactCollection.at('0').get('name'), 'Á'); + // assert.strictEqual(convo.contactCollection.at('1').get('name'), 'B'); + // assert.strictEqual(convo.contactCollection.at('2').get('name'), 'C'); + // }); + // it('contains its own messages', async () => { + // const convo = new Whisper.ConversationCollection().add({ + // id: '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // await convo.fetchMessages(); + // assert.notEqual(convo.messageCollection.length, 0); + // }); + // it('contains only its own messages', async () => { + // const convo = new Whisper.ConversationCollection().add({ + // id: '052d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // await convo.fetchMessages(); + // assert.strictEqual(convo.messageCollection.length, 0); + // }); + // it('adds conversation to message collection upon leaving group', async () => { + // const convo = new Whisper.ConversationCollection().add({ + // type: 'group', + // id: '052d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // await convo.leaveGroup(); + // assert.notEqual(convo.messageCollection.length, 0); + // }); + // it('has a title', () => { + // const convos = new Whisper.ConversationCollection(); + // let convo = convos.add(attributes); + // assert.equal( + // convo.getTitle(), + // '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab' + // ); + // convo = convos.add({ type: '' }); + // assert.equal(convo.getTitle(), 'Unknown group'); + // convo = convos.add({ name: 'name' }); + // assert.equal(convo.getTitle(), 'name'); + // }); + // it('returns the number', () => { + // const convos = new Whisper.ConversationCollection(); + // let convo = convos.add(attributes); + // assert.equal( + // convo.getNumber(), + // '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab' + // ); + // convo = convos.add({ type: '' }); + // assert.equal(convo.getNumber(), ''); + // }); + // describe('when set to private', () => { + // it('correctly validates hex numbers', () => { + // const regularId = new Whisper.Conversation({ + // type: 'private', + // id: + // '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // const invalidId = new Whisper.Conversation({ + // type: 'private', + // id: + // 'j71d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // assert.ok(regularId.isValid()); + // assert.notOk(invalidId.isValid()); + // }); + // it('correctly validates length', () => { + // const regularId33 = new Whisper.Conversation({ + // type: 'private', + // id: + // '051d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // const regularId32 = new Whisper.Conversation({ + // type: 'private', + // id: '1d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94ab', + // }); + // const shortId = new Whisper.Conversation({ + // type: 'private', + // id: '771d11d', + // }); + // const longId = new Whisper.Conversation({ + // type: 'private', + // id: + // '771d11d01e56d9bfc3d74115c33225a632321b509ac17a13fdeac71165d09b94abaa', + // }); + // assert.ok(regularId33.isValid()); + // assert.ok(regularId32.isValid()); + // assert.notOk(shortId.isValid()); + // assert.notOk(longId.isValid()); + // }); + // }); +}); diff --git a/ts/test/test-electron/linkPreviews/linkPreviewFetch_test.ts b/ts/test/test-electron/linkPreviews/linkPreviewFetch_test.ts index 377672ce9..31c3c5201 100644 --- a/ts/test/test-electron/linkPreviews/linkPreviewFetch_test.ts +++ b/ts/test/test-electron/linkPreviews/linkPreviewFetch_test.ts @@ -4,7 +4,7 @@ // import * as path from 'path'; // import AbortController from 'abort-controller'; // import { IMAGE_JPEG, MIMEType } from '../../../types/MIME'; -// FIXME audric enable back those test +// // FIXME audric enable back those test // import { // fetchLinkPreviewImage, // fetchLinkPreviewMetadata, @@ -12,1287 +12,1153 @@ // import { TestUtils } from '../../test-utils'; // import { globalAny } from '../../test-utils/utils'; -// // tslint:disable: no-http-string - -// describe('link preview fetching', () => { -// // We'll use this to create a fake `fetch`. We'll want to call `.resolves` or -// // `.rejects` on it (meaning that it needs to be a Sinon Stub type), but we'll also -// // want it to be a fake `fetch`. `any` seems like the best "supertype" there. -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// function stub(): any { -// return sinon.stub(); -// } - -// let sandbox: sinon.SinonSandbox; -// let warn: sinon.SinonStub; - -// beforeEach(() => { -// sandbox = sinon.createSandbox(); -// warn = sandbox.stub(window.log, 'warn'); -// }); - -// afterEach(() => { -// sandbox.restore(); -// }); - -// // tslint:disable-next-line: max-func-body-length -// describe('fetchLinkPreviewMetadata', () => { -// const makeHtml = (stuffInHead: ReadonlyArray = []) => ` -// -// -// ${stuffInHead.join('\n')} -// should be ignored -// -// `; - -// const makeResponse = ({ -// status = 200, -// headers = {}, -// body = makeHtml(['test title']), -// url = 'https://example.com', -// }: { -// status?: number; -// headers?: { [key: string]: null | string }; -// body?: null | string | Uint8Array | AsyncIterable; -// url?: string; -// } = {}) => { -// let bodyLength: null | number; -// let bodyStream: null | AsyncIterable; -// if (!body) { -// bodyLength = 0; -// bodyStream = null; -// } else if (typeof body === 'string') { -// const asBytes = new TextEncoder().encode(body); -// bodyLength = asBytes.length; -// bodyStream = (async function* stream() { -// yield asBytes; -// })(); -// } else if (body instanceof Uint8Array) { -// bodyLength = body.length; -// bodyStream = (async function* stream() { -// yield body; -// })(); -// } else { -// bodyLength = null; -// bodyStream = body; -// } - -// const headersObj = new Headers(); -// Object.entries({ -// 'Content-Type': 'text/html; charset=utf-8', -// 'Content-Length': bodyLength === null ? null : String(bodyLength), -// ...headers, -// }).forEach(([headerName, headerValue]) => { -// if (headerValue) { -// headersObj.set(headerName, headerValue); -// } -// }); - -// return { -// headers: headersObj, -// body: bodyStream, -// ok: status >= 200 && status <= 299, -// status, -// url, -// }; -// }; - -// it('handles the "kitchen sink" of results', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// '', -// '', -// '', -// '', -// ]), -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'test title', -// description: 'test description', -// date: 1587386096009, -// imageHref: 'https://example.com/image.jpg', -// } -// ); -// }); - -// it('logs no warnings if everything goes smoothly', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// '', -// '', -// '', -// '', -// ]), -// }) -// ); - -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ); - -// sinon.assert.notCalled(warn); -// }); - -// it('sends "WhatsApp" as the User-Agent for compatibility', async () => { -// const fakeFetch = stub().resolves(makeResponse()); - -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ); - -// sinon.assert.calledWith( -// fakeFetch, -// 'https://example.com', -// sinon.match({ -// headers: { -// 'User-Agent': 'WhatsApp', -// }, -// }) -// ); -// }); - -// it('returns null if the request fails', async () => { -// const fakeFetch = stub().rejects(new Error('Test request failure')); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewMetadata: failed to fetch link preview HTML; bailing' -// ); -// }); - -// it("returns null if the response status code isn't 2xx", async () => { -// await Promise.all( -// [100, 304, 400, 404, 500, 0, -200].map(async status => { -// const fakeFetch = stub().resolves(makeResponse({ status })); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledWith( -// warn, -// `fetchLinkPreviewMetadata: got a ${status} status code; bailing` -// ); -// }) -// ); -// }); - -// it("doesn't use fetch's automatic redirection behavior", async () => { -// const fakeFetch = stub().resolves(makeResponse()); - -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ); - -// sinon.assert.calledWith( -// fakeFetch, -// 'https://example.com', -// sinon.match({ redirect: 'manual' }) -// ); -// }); - -// [301, 302, 303, 307, 308].forEach(status => { -// it(`handles ${status} redirects`, async () => { -// const fakeFetch = stub(); -// fakeFetch.onFirstCall().resolves( -// makeResponse({ -// status, -// headers: { Location: 'https://example.com/2' }, -// body: null, -// }) -// ); -// fakeFetch.onSecondCall().resolves(makeResponse()); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'test title', -// description: null, -// date: null, -// imageHref: null, -// } -// ); - -// sinon.assert.calledTwice(fakeFetch); -// sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com'); -// sinon.assert.calledWith(fakeFetch.getCall(1), 'https://example.com/2'); -// }); - -// it(`returns null when seeing a ${status} status with no Location header`, async () => { -// const fakeFetch = stub().resolves(makeResponse({ status })); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); -// }); -// }); - -// it('handles relative redirects', async () => { -// const fakeFetch = stub(); -// fakeFetch.onFirstCall().resolves( -// makeResponse({ -// status: 301, -// headers: { Location: '/2/' }, -// body: null, -// }) -// ); -// fakeFetch.onSecondCall().resolves( -// makeResponse({ -// status: 301, -// headers: { Location: '3' }, -// body: null, -// }) -// ); -// fakeFetch.onThirdCall().resolves(makeResponse()); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'test title', -// description: null, -// date: null, -// imageHref: null, -// } -// ); - -// sinon.assert.calledThrice(fakeFetch); -// sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com'); -// sinon.assert.calledWith(fakeFetch.getCall(1), 'https://example.com/2/'); -// sinon.assert.calledWith(fakeFetch.getCall(2), 'https://example.com/2/3'); -// }); - -// it('returns null if redirecting to an insecure HTTP URL', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// status: 301, -// headers: { Location: 'http://example.com' }, -// body: null, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(fakeFetch); -// }); - -// it("returns null if there's a redirection loop", async () => { -// const fakeFetch = stub(); -// fakeFetch.onFirstCall().resolves( -// makeResponse({ -// status: 301, -// headers: { Location: '/2/' }, -// body: null, -// }) -// ); -// fakeFetch.onSecondCall().resolves( -// makeResponse({ -// status: 301, -// headers: { Location: '/start' }, -// body: null, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com/start', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledTwice(fakeFetch); -// }); - -// it('returns null if redirecting more than 20 times', async () => { -// const fakeFetch = stub().callsFake(async () => -// makeResponse({ -// status: 301, -// // tslint:disable-next-line: insecure-random -// headers: { Location: `/${Math.random()}` }, -// body: null, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com/start', -// new AbortController().signal -// ) -// ); - -// sinon.assert.callCount(fakeFetch, 20); -// }); - -// it('returns null if the response has no body', async () => { -// const fakeFetch = stub().resolves(makeResponse({ body: null })); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewMetadata: no response body; bailing' -// ); -// }); - -// it('returns null if the result body is too short', async () => { -// const fakeFetch = stub().resolves(makeResponse({ body: '' })); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewMetadata: Content-Length is too short; bailing' -// ); -// }); - -// it('returns null if the result is meant to be downloaded', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { 'Content-Disposition': 'attachment' }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewMetadata: Content-Disposition header is not inline; bailing' -// ); -// }); - -// it('allows an explitly inline Content-Disposition header', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { 'Content-Disposition': 'inline' }, -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'test title', -// description: null, -// date: null, -// imageHref: null, -// } -// ); -// }); - -// it('returns null if the Content-Type is not HTML', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { 'Content-Type': 'text/plain' }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewMetadata: Content-Type is not HTML; bailing' -// ); -// }); - -// it('accepts non-lowercase Content-Type headers', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { 'Content-Type': 'TEXT/HTML; chArsEt=utf-8' }, -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'test title', -// description: null, -// date: null, -// imageHref: null, -// } -// ); -// }); - -// it('parses the response as UTF-8 if the body contains a byte order mark', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html', -// }, -// body: (async function* body() { -// yield new Uint8Array([0xef, 0xbb, 0xbf]); -// yield new TextEncoder().encode( -// '<!doctype html><title>\u{1F389}' -// ); -// })(), -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: '🎉', -// description: null, -// date: null, -// imageHref: null, -// } -// ); -// }); - -// it('respects the UTF-8 byte order mark above the Content-Type header', async () => { -// const bom = new Uint8Array([0xef, 0xbb, 0xbf]); -// const titleHtml = new TextEncoder().encode('\u{1F389}'); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html; charset=latin1', -// }, -// body: (async function* body() { -// yield bom; -// yield titleHtml; -// })(), -// }) -// ); -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// }); - -// it('respects the UTF-8 byte order mark above a in the document', async () => { -// const bom = new Uint8Array([0xef, 0xbb, 0xbf]); -// const titleHtml = new TextEncoder().encode('\u{1F389}'); -// const endHeadHtml = new TextEncoder().encode(''); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html', -// }, -// body: (async function* body() { -// yield bom; -// yield new TextEncoder().encode( -// '' -// ); -// yield titleHtml; -// yield endHeadHtml; -// })(), -// }) -// ); -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// }); - -// it('respects the UTF-8 byte order mark above a in the document', async () => { -// const bom = new Uint8Array([0xef, 0xbb, 0xbf]); -// const titleHtml = new TextEncoder().encode('\u{1F389}'); -// const endHeadHtml = new TextEncoder().encode(''); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html', -// }, -// body: (async function* body() { -// yield bom; -// yield new TextEncoder().encode( -// '' -// ); -// yield titleHtml; -// yield endHeadHtml; -// })(), -// }) -// ); -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// }); - -// it('respects the Content-Type header above anything in the HTML', async () => { -// const titleHtml = new TextEncoder().encode('\u{1F389}'); -// const endHeadHtml = new TextEncoder().encode(''); - -// { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html; charset=utf-8', -// }, -// body: (async function* body() { -// yield new TextEncoder().encode( -// '' -// ); -// yield titleHtml; -// yield endHeadHtml; -// })(), -// }) -// ); -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// } - -// { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html; charset=utf-8', -// }, -// body: (async function* body() { -// yield new TextEncoder().encode( -// '' -// ); -// yield titleHtml; -// yield endHeadHtml; -// })(), -// }) -// ); -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// } -// }); - -// it('prefers the Content-Type http-equiv in the HTML above ', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html', -// }, -// body: makeHtml([ -// '', -// '', -// '\u{1F389}', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// '🎉' -// ); -// }); - -// it('parses non-UTF8 encodings', async () => { -// const titleBytes = new Uint8Array([0x61, 0x71, 0x75, 0xed]); -// assert.notDeepEqual( -// new TextDecoder('utf8').decode(titleBytes), -// new TextDecoder('latin1').decode(titleBytes), -// 'Test data was not set up correctly' -// ); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// headers: { -// 'Content-Type': 'text/html; charset=latin1', -// }, -// body: (async function* body() { -// yield new TextEncoder().encode(''); -// yield titleBytes; -// yield new TextEncoder().encode(''); -// })(), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// 'aquí' -// ); -// }); - -// it('handles incomplete bodies', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: (async function* body() { -// yield new TextEncoder().encode( -// 'foo bar { -// const shouldNeverBeCalled = sinon.stub(); - -// const abortController = new AbortController(); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: (async function* body() { -// yield new TextEncoder().encode(''); -// abortController.abort(); -// yield new TextEncoder().encode('should be dropped'); -// shouldNeverBeCalled(); -// })(), -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// abortController.signal -// ) -// ); - -// sinon.assert.notCalled(shouldNeverBeCalled); -// }); - -// it('stops reading bodies after 500 kilobytes', async () => { -// const shouldNeverBeCalled = sinon.stub(); - -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: (async function* body() { -// yield new TextEncoder().encode( -// 'foo bar' -// ); -// const spaces = new Uint8Array(1024).fill(32); -// for (let i = 0; i < 500; i += 1) { -// yield spaces; -// } -// shouldNeverBeCalled(); -// yield new TextEncoder().encode( -// '' -// ); -// })(), -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// { -// title: 'foo bar', -// description: null, -// date: null, -// imageHref: null, -// } -// ); - -// sinon.assert.notCalled(shouldNeverBeCalled); -// }); - -// it("returns null if the HTML doesn't contain a title, even if it contains other values", async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// '', -// '', -// ``, -// ]), -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// "parseMetadata: HTML document doesn't have a title; bailing" -// ); -// }); - -// it('prefers og:title to document.title', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'ignored', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'title', -// 'foo bar' -// ); -// }); - -// it('prefers og:description to ', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'description', -// 'bar' -// ); -// }); - -// it('parses ', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'description', -// 'bar' -// ); -// }); - -// it('ignores empty descriptions', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'description', -// null -// ); -// }); - -// it('parses absolute image URLs', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'imageHref', -// 'https://example.com/image.jpg' -// ); -// }); - -// it('parses relative image URLs', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'imageHref', -// 'https://example.com/assets/image.jpg' -// ); -// }); - -// it('relative image URL resolution is relative to the final URL after redirects, not the original URL', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// url: 'https://bar.example/assets/', -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://foo.example', -// new AbortController().signal -// ), -// 'imageHref', -// 'https://bar.example/assets/image.jpg' -// ); -// }); - -// it('ignores empty image URLs', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'imageHref', -// null -// ); -// }); - -// it('ignores blank image URLs', async () => { -// const fakeFetch = stub().resolves( -// makeResponse({ -// body: makeHtml([ -// 'foo', -// '', -// ]), -// }) -// ); - -// assert.propertyVal( -// await fetchLinkPreviewMetadata( -// fakeFetch, -// 'https://example.com', -// new AbortController().signal -// ), -// 'imageHref', -// null -// ); -// }); -// }); - -// // tslint:disable-next-line: max-func-body-length -// describe('fetchLinkPreviewImage', () => { -// const readFixture = async (filename: string): Promise => { -// const result = await fs.promises.readFile( -// path.join(__dirname, '..', '..', '..', 'fixtures', filename) -// ); -// assert(result.length > 10, `Test failed to read fixture ${filename}`); -// return result; -// }; - -// [ -// { -// title: 'JPEG', -// contentType: 'image/jpeg', -// fixtureFilename: 'kitten-1-64-64.jpg', -// }, -// { -// title: 'PNG', -// contentType: 'image/png', -// fixtureFilename: -// 'freepngs-2cd43b_bed7d1327e88454487397574d87b64dc_mv2.png', -// }, -// { -// title: 'GIF', -// contentType: 'image/gif', -// fixtureFilename: 'giphy-GVNvOUpeYmI7e.gif', -// }, -// { -// title: 'WEBP', -// contentType: 'image/webp', -// fixtureFilename: '512x515-thumbs-up-lincoln.webp', -// }, -// { -// title: 'ICO', -// contentType: 'image/x-icon', -// fixtureFilename: 'kitten-1-64-64.ico', -// }, -// ].forEach(({ title, contentType, fixtureFilename }) => { -// it(`handles ${title} images`, async () => { -// const fixture = await readFixture(fixtureFilename); - -// const fakeFetch = stub().resolves( -// new Response(fixture, { -// headers: { -// 'Content-Type': contentType, -// 'Content-Length': fixture.length.toString(), -// }, -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ), -// { -// data: fixture.buffer, -// contentType: contentType, -// } -// ); -// }); -// }); - -// it('returns null if the request fails', async () => { -// const fakeFetch = stub().rejects(new Error('Test request failure')); - -// assert.isNull( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewImage: failed to fetch image; bailing' -// ); -// }); - -// it("returns null if the response status code isn't 2xx", async () => { -// const fixture = await readFixture('kitten-1-64-64.jpg'); - -// await Promise.all( -// [400, 404, 500, 598].map(async status => { -// const fakeFetch = stub().resolves( -// new Response(fixture, { -// status, -// headers: { -// 'Content-Type': 'image/jpeg', -// 'Content-Length': fixture.length.toString(), -// }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledWith( -// warn, -// `fetchLinkPreviewImage: got a ${status} status code; bailing` -// ); -// }) -// ); -// }); - -// // Most of the redirect behavior is tested above. -// it('handles 301 redirects', async () => { -// const fixture = await readFixture('kitten-1-64-64.jpg'); - -// const fakeFetch = stub(); -// fakeFetch.onFirstCall().resolves( -// new Response(null, { -// status: 301, -// headers: { -// Location: '/result.jpg', -// }, -// }) -// ); -// fakeFetch.onSecondCall().resolves( -// new Response(fixture, { -// headers: { -// 'Content-Type': IMAGE_JPEG, -// 'Content-Length': fixture.length.toString(), -// }, -// }) -// ); - -// assert.deepEqual( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ), -// { -// data: fixture.buffer, -// contentType: IMAGE_JPEG, -// } -// ); - -// sinon.assert.calledTwice(fakeFetch); -// sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com/img'); -// sinon.assert.calledWith( -// fakeFetch.getCall(1), -// 'https://example.com/result.jpg' -// ); -// }); - -// it('returns null if the response is too small', async () => { -// const fakeFetch = stub().resolves( -// new Response(await readFixture('kitten-1-64-64.jpg'), { -// headers: { -// 'Content-Type': 'image/jpeg', -// 'Content-Length': '2', -// }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewImage: Content-Length is too short; bailing' -// ); -// }); - -// it('returns null if the response is too large', async () => { -// const fakeFetch = stub().resolves( -// new Response(await readFixture('kitten-1-64-64.jpg'), { -// headers: { -// 'Content-Type': 'image/jpeg', -// 'Content-Length': '123456789', -// }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledOnce(warn); -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewImage: Content-Length is too large or is unset; bailing' -// ); -// }); - -// it('returns null if the Content-Type is not a valid image', async () => { -// const fixture = await readFixture('kitten-1-64-64.jpg'); - -// await Promise.all( -// ['', 'image/tiff', 'video/mp4', 'text/plain', 'application/html'].map( -// async contentType => { -// const fakeFetch = stub().resolves( -// new Response(fixture, { -// headers: { -// 'Content-Type': contentType, -// 'Content-Length': fixture.length.toString(), -// }, -// }) -// ); - -// assert.isNull( -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ) -// ); - -// sinon.assert.calledWith( -// warn, -// 'fetchLinkPreviewImage: Content-Type is not an image; bailing' -// ); -// } -// ) -// ); -// }); - -// it('sends "WhatsApp" as the User-Agent for compatibility', async () => { -// const fakeFetch = stub().resolves(new Response(null)); - -// await fetchLinkPreviewImage( -// fakeFetch, -// 'https://example.com/img', -// new AbortController().signal -// ); - -// sinon.assert.calledWith( -// fakeFetch, -// 'https://example.com/img', -// sinon.match({ -// headers: { -// 'User-Agent': 'WhatsApp', -// }, -// }) -// ); -// }); -// }); -// }); +// tslint:disable: no-http-string + +describe('link preview fetching', () => { + // We'll use this to create a fake `fetch`. We'll want to call `.resolves` or + // `.rejects` on it (meaning that it needs to be a Sinon Stub type), but we'll also + // want it to be a fake `fetch`. `any` seems like the best "supertype" there. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + // function stub(): any { + // return sinon.stub(); + // } + // let sandbox: sinon.SinonSandbox; + // let warn: sinon.SinonStub; + // beforeEach(() => { + // sandbox = sinon.createSandbox(); + // warn = sandbox.stub(window.log, 'warn'); + // }); + // afterEach(() => { + // sandbox.restore(); + // }); + // // tslint:disable-next-line: max-func-body-length + // describe('fetchLinkPreviewMetadata', () => { + // const makeHtml = (stuffInHead: ReadonlyArray = []) => ` + // + // + // ${stuffInHead.join('\n')} + // should be ignored + // + // `; + // const makeResponse = ({ + // status = 200, + // headers = {}, + // body = makeHtml(['test title']), + // url = 'https://example.com', + // }: { + // status?: number; + // headers?: { [key: string]: null | string }; + // body?: null | string | Uint8Array | AsyncIterable; + // url?: string; + // } = {}) => { + // let bodyLength: null | number; + // let bodyStream: null | AsyncIterable; + // if (!body) { + // bodyLength = 0; + // bodyStream = null; + // } else if (typeof body === 'string') { + // const asBytes = new TextEncoder().encode(body); + // bodyLength = asBytes.length; + // bodyStream = (async function* stream() { + // yield asBytes; + // })(); + // } else if (body instanceof Uint8Array) { + // bodyLength = body.length; + // bodyStream = (async function* stream() { + // yield body; + // })(); + // } else { + // bodyLength = null; + // bodyStream = body; + // } + // const headersObj = new Headers(); + // Object.entries({ + // 'Content-Type': 'text/html; charset=utf-8', + // 'Content-Length': bodyLength === null ? null : String(bodyLength), + // ...headers, + // }).forEach(([headerName, headerValue]) => { + // if (headerValue) { + // headersObj.set(headerName, headerValue); + // } + // }); + // return { + // headers: headersObj, + // body: bodyStream, + // ok: status >= 200 && status <= 299, + // status, + // url, + // }; + // }; + // it('handles the "kitchen sink" of results', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // '', + // '', + // '', + // '', + // ]), + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'test title', + // description: 'test description', + // date: 1587386096009, + // imageHref: 'https://example.com/image.jpg', + // } + // ); + // }); + // it('logs no warnings if everything goes smoothly', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // '', + // '', + // '', + // '', + // ]), + // }) + // ); + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ); + // sinon.assert.notCalled(warn); + // }); + // it('sends "WhatsApp" as the User-Agent for compatibility', async () => { + // const fakeFetch = stub().resolves(makeResponse()); + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ); + // sinon.assert.calledWith( + // fakeFetch, + // 'https://example.com', + // sinon.match({ + // headers: { + // 'User-Agent': 'WhatsApp', + // }, + // }) + // ); + // }); + // it('returns null if the request fails', async () => { + // const fakeFetch = stub().rejects(new Error('Test request failure')); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewMetadata: failed to fetch link preview HTML; bailing' + // ); + // }); + // it("returns null if the response status code isn't 2xx", async () => { + // await Promise.all( + // [100, 304, 400, 404, 500, 0, -200].map(async status => { + // const fakeFetch = stub().resolves(makeResponse({ status })); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledWith( + // warn, + // `fetchLinkPreviewMetadata: got a ${status} status code; bailing` + // ); + // }) + // ); + // }); + // it("doesn't use fetch's automatic redirection behavior", async () => { + // const fakeFetch = stub().resolves(makeResponse()); + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ); + // sinon.assert.calledWith( + // fakeFetch, + // 'https://example.com', + // sinon.match({ redirect: 'manual' }) + // ); + // }); + // [301, 302, 303, 307, 308].forEach(status => { + // it(`handles ${status} redirects`, async () => { + // const fakeFetch = stub(); + // fakeFetch.onFirstCall().resolves( + // makeResponse({ + // status, + // headers: { Location: 'https://example.com/2' }, + // body: null, + // }) + // ); + // fakeFetch.onSecondCall().resolves(makeResponse()); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'test title', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // sinon.assert.calledTwice(fakeFetch); + // sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com'); + // sinon.assert.calledWith(fakeFetch.getCall(1), 'https://example.com/2'); + // }); + // it(`returns null when seeing a ${status} status with no Location header`, async () => { + // const fakeFetch = stub().resolves(makeResponse({ status })); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // }); + // }); + // it('handles relative redirects', async () => { + // const fakeFetch = stub(); + // fakeFetch.onFirstCall().resolves( + // makeResponse({ + // status: 301, + // headers: { Location: '/2/' }, + // body: null, + // }) + // ); + // fakeFetch.onSecondCall().resolves( + // makeResponse({ + // status: 301, + // headers: { Location: '3' }, + // body: null, + // }) + // ); + // fakeFetch.onThirdCall().resolves(makeResponse()); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'test title', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // sinon.assert.calledThrice(fakeFetch); + // sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com'); + // sinon.assert.calledWith(fakeFetch.getCall(1), 'https://example.com/2/'); + // sinon.assert.calledWith(fakeFetch.getCall(2), 'https://example.com/2/3'); + // }); + // it('returns null if redirecting to an insecure HTTP URL', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // status: 301, + // headers: { Location: 'http://example.com' }, + // body: null, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(fakeFetch); + // }); + // it("returns null if there's a redirection loop", async () => { + // const fakeFetch = stub(); + // fakeFetch.onFirstCall().resolves( + // makeResponse({ + // status: 301, + // headers: { Location: '/2/' }, + // body: null, + // }) + // ); + // fakeFetch.onSecondCall().resolves( + // makeResponse({ + // status: 301, + // headers: { Location: '/start' }, + // body: null, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com/start', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledTwice(fakeFetch); + // }); + // it('returns null if redirecting more than 20 times', async () => { + // const fakeFetch = stub().callsFake(async () => + // makeResponse({ + // status: 301, + // // tslint:disable-next-line: insecure-random + // headers: { Location: `/${Math.random()}` }, + // body: null, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com/start', + // new AbortController().signal + // ) + // ); + // sinon.assert.callCount(fakeFetch, 20); + // }); + // it('returns null if the response has no body', async () => { + // const fakeFetch = stub().resolves(makeResponse({ body: null })); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewMetadata: no response body; bailing' + // ); + // }); + // it('returns null if the result body is too short', async () => { + // const fakeFetch = stub().resolves(makeResponse({ body: '' })); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewMetadata: Content-Length is too short; bailing' + // ); + // }); + // it('returns null if the result is meant to be downloaded', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { 'Content-Disposition': 'attachment' }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewMetadata: Content-Disposition header is not inline; bailing' + // ); + // }); + // it('allows an explitly inline Content-Disposition header', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { 'Content-Disposition': 'inline' }, + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'test title', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // }); + // it('returns null if the Content-Type is not HTML', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { 'Content-Type': 'text/plain' }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewMetadata: Content-Type is not HTML; bailing' + // ); + // }); + // it('accepts non-lowercase Content-Type headers', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { 'Content-Type': 'TEXT/HTML; chArsEt=utf-8' }, + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'test title', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // }); + // it('parses the response as UTF-8 if the body contains a byte order mark', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html', + // }, + // body: (async function* body() { + // yield new Uint8Array([0xef, 0xbb, 0xbf]); + // yield new TextEncoder().encode( + // '<!doctype html><title>\u{1F389}' + // ); + // })(), + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: '🎉', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // }); + // it('respects the UTF-8 byte order mark above the Content-Type header', async () => { + // const bom = new Uint8Array([0xef, 0xbb, 0xbf]); + // const titleHtml = new TextEncoder().encode('\u{1F389}'); + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html; charset=latin1', + // }, + // body: (async function* body() { + // yield bom; + // yield titleHtml; + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // }); + // it('respects the UTF-8 byte order mark above a in the document', async () => { + // const bom = new Uint8Array([0xef, 0xbb, 0xbf]); + // const titleHtml = new TextEncoder().encode('\u{1F389}'); + // const endHeadHtml = new TextEncoder().encode(''); + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html', + // }, + // body: (async function* body() { + // yield bom; + // yield new TextEncoder().encode( + // '' + // ); + // yield titleHtml; + // yield endHeadHtml; + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // }); + // it('respects the UTF-8 byte order mark above a in the document', async () => { + // const bom = new Uint8Array([0xef, 0xbb, 0xbf]); + // const titleHtml = new TextEncoder().encode('\u{1F389}'); + // const endHeadHtml = new TextEncoder().encode(''); + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html', + // }, + // body: (async function* body() { + // yield bom; + // yield new TextEncoder().encode( + // '' + // ); + // yield titleHtml; + // yield endHeadHtml; + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // }); + // it('respects the Content-Type header above anything in the HTML', async () => { + // const titleHtml = new TextEncoder().encode('\u{1F389}'); + // const endHeadHtml = new TextEncoder().encode(''); + // { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html; charset=utf-8', + // }, + // body: (async function* body() { + // yield new TextEncoder().encode( + // '' + // ); + // yield titleHtml; + // yield endHeadHtml; + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // } + // { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html; charset=utf-8', + // }, + // body: (async function* body() { + // yield new TextEncoder().encode( + // '' + // ); + // yield titleHtml; + // yield endHeadHtml; + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // } + // }); + // it('prefers the Content-Type http-equiv in the HTML above ', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html', + // }, + // body: makeHtml([ + // '', + // '', + // '\u{1F389}', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // '🎉' + // ); + // }); + // it('parses non-UTF8 encodings', async () => { + // const titleBytes = new Uint8Array([0x61, 0x71, 0x75, 0xed]); + // assert.notDeepEqual( + // new TextDecoder('utf8').decode(titleBytes), + // new TextDecoder('latin1').decode(titleBytes), + // 'Test data was not set up correctly' + // ); + // const fakeFetch = stub().resolves( + // makeResponse({ + // headers: { + // 'Content-Type': 'text/html; charset=latin1', + // }, + // body: (async function* body() { + // yield new TextEncoder().encode(''); + // yield titleBytes; + // yield new TextEncoder().encode(''); + // })(), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // 'aquí' + // ); + // }); + // it('handles incomplete bodies', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: (async function* body() { + // yield new TextEncoder().encode( + // 'foo bar { + // const shouldNeverBeCalled = sinon.stub(); + // const abortController = new AbortController(); + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: (async function* body() { + // yield new TextEncoder().encode(''); + // abortController.abort(); + // yield new TextEncoder().encode('should be dropped'); + // shouldNeverBeCalled(); + // })(), + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // abortController.signal + // ) + // ); + // sinon.assert.notCalled(shouldNeverBeCalled); + // }); + // it('stops reading bodies after 500 kilobytes', async () => { + // const shouldNeverBeCalled = sinon.stub(); + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: (async function* body() { + // yield new TextEncoder().encode( + // 'foo bar' + // ); + // const spaces = new Uint8Array(1024).fill(32); + // for (let i = 0; i < 500; i += 1) { + // yield spaces; + // } + // shouldNeverBeCalled(); + // yield new TextEncoder().encode( + // '' + // ); + // })(), + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // { + // title: 'foo bar', + // description: null, + // date: null, + // imageHref: null, + // } + // ); + // sinon.assert.notCalled(shouldNeverBeCalled); + // }); + // it("returns null if the HTML doesn't contain a title, even if it contains other values", async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // '', + // '', + // ``, + // ]), + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // "parseMetadata: HTML document doesn't have a title; bailing" + // ); + // }); + // it('prefers og:title to document.title', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'ignored', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'title', + // 'foo bar' + // ); + // }); + // it('prefers og:description to ', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'description', + // 'bar' + // ); + // }); + // it('parses ', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'description', + // 'bar' + // ); + // }); + // it('ignores empty descriptions', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'description', + // null + // ); + // }); + // it('parses absolute image URLs', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'imageHref', + // 'https://example.com/image.jpg' + // ); + // }); + // it('parses relative image URLs', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'imageHref', + // 'https://example.com/assets/image.jpg' + // ); + // }); + // it('relative image URL resolution is relative to the final URL after redirects, not the original URL', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // url: 'https://bar.example/assets/', + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://foo.example', + // new AbortController().signal + // ), + // 'imageHref', + // 'https://bar.example/assets/image.jpg' + // ); + // }); + // it('ignores empty image URLs', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'imageHref', + // null + // ); + // }); + // it('ignores blank image URLs', async () => { + // const fakeFetch = stub().resolves( + // makeResponse({ + // body: makeHtml([ + // 'foo', + // '', + // ]), + // }) + // ); + // assert.propertyVal( + // await fetchLinkPreviewMetadata( + // fakeFetch, + // 'https://example.com', + // new AbortController().signal + // ), + // 'imageHref', + // null + // ); + // }); + // }); + // // tslint:disable-next-line: max-func-body-length + // describe('fetchLinkPreviewImage', () => { + // const readFixture = async (filename: string): Promise => { + // const result = await fs.promises.readFile( + // path.join(__dirname, '..', '..', '..', 'fixtures', filename) + // ); + // assert(result.length > 10, `Test failed to read fixture ${filename}`); + // return result; + // }; + // [ + // { + // title: 'JPEG', + // contentType: 'image/jpeg', + // fixtureFilename: 'kitten-1-64-64.jpg', + // }, + // { + // title: 'PNG', + // contentType: 'image/png', + // fixtureFilename: + // 'freepngs-2cd43b_bed7d1327e88454487397574d87b64dc_mv2.png', + // }, + // { + // title: 'GIF', + // contentType: 'image/gif', + // fixtureFilename: 'giphy-GVNvOUpeYmI7e.gif', + // }, + // { + // title: 'WEBP', + // contentType: 'image/webp', + // fixtureFilename: '512x515-thumbs-up-lincoln.webp', + // }, + // { + // title: 'ICO', + // contentType: 'image/x-icon', + // fixtureFilename: 'kitten-1-64-64.ico', + // }, + // ].forEach(({ title, contentType, fixtureFilename }) => { + // it(`handles ${title} images`, async () => { + // const fixture = await readFixture(fixtureFilename); + // const fakeFetch = stub().resolves( + // new Response(fixture, { + // headers: { + // 'Content-Type': contentType, + // 'Content-Length': fixture.length.toString(), + // }, + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ), + // { + // data: fixture.buffer, + // contentType: contentType, + // } + // ); + // }); + // }); + // it('returns null if the request fails', async () => { + // const fakeFetch = stub().rejects(new Error('Test request failure')); + // assert.isNull( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewImage: failed to fetch image; bailing' + // ); + // }); + // it("returns null if the response status code isn't 2xx", async () => { + // const fixture = await readFixture('kitten-1-64-64.jpg'); + // await Promise.all( + // [400, 404, 500, 598].map(async status => { + // const fakeFetch = stub().resolves( + // new Response(fixture, { + // status, + // headers: { + // 'Content-Type': 'image/jpeg', + // 'Content-Length': fixture.length.toString(), + // }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledWith( + // warn, + // `fetchLinkPreviewImage: got a ${status} status code; bailing` + // ); + // }) + // ); + // }); + // // Most of the redirect behavior is tested above. + // it('handles 301 redirects', async () => { + // const fixture = await readFixture('kitten-1-64-64.jpg'); + // const fakeFetch = stub(); + // fakeFetch.onFirstCall().resolves( + // new Response(null, { + // status: 301, + // headers: { + // Location: '/result.jpg', + // }, + // }) + // ); + // fakeFetch.onSecondCall().resolves( + // new Response(fixture, { + // headers: { + // 'Content-Type': IMAGE_JPEG, + // 'Content-Length': fixture.length.toString(), + // }, + // }) + // ); + // assert.deepEqual( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ), + // { + // data: fixture.buffer, + // contentType: IMAGE_JPEG, + // } + // ); + // sinon.assert.calledTwice(fakeFetch); + // sinon.assert.calledWith(fakeFetch.getCall(0), 'https://example.com/img'); + // sinon.assert.calledWith( + // fakeFetch.getCall(1), + // 'https://example.com/result.jpg' + // ); + // }); + // it('returns null if the response is too small', async () => { + // const fakeFetch = stub().resolves( + // new Response(await readFixture('kitten-1-64-64.jpg'), { + // headers: { + // 'Content-Type': 'image/jpeg', + // 'Content-Length': '2', + // }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewImage: Content-Length is too short; bailing' + // ); + // }); + // it('returns null if the response is too large', async () => { + // const fakeFetch = stub().resolves( + // new Response(await readFixture('kitten-1-64-64.jpg'), { + // headers: { + // 'Content-Type': 'image/jpeg', + // 'Content-Length': '123456789', + // }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledOnce(warn); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewImage: Content-Length is too large or is unset; bailing' + // ); + // }); + // it('returns null if the Content-Type is not a valid image', async () => { + // const fixture = await readFixture('kitten-1-64-64.jpg'); + // await Promise.all( + // ['', 'image/tiff', 'video/mp4', 'text/plain', 'application/html'].map( + // async contentType => { + // const fakeFetch = stub().resolves( + // new Response(fixture, { + // headers: { + // 'Content-Type': contentType, + // 'Content-Length': fixture.length.toString(), + // }, + // }) + // ); + // assert.isNull( + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ) + // ); + // sinon.assert.calledWith( + // warn, + // 'fetchLinkPreviewImage: Content-Type is not an image; bailing' + // ); + // } + // ) + // ); + // }); + // it('sends "WhatsApp" as the User-Agent for compatibility', async () => { + // const fakeFetch = stub().resolves(new Response(null)); + // await fetchLinkPreviewImage( + // fakeFetch, + // 'https://example.com/img', + // new AbortController().signal + // ); + // sinon.assert.calledWith( + // fakeFetch, + // 'https://example.com/img', + // sinon.match({ + // headers: { + // 'User-Agent': 'WhatsApp', + // }, + // }) + // ); + // }); + // }); +});