Compare commits
2 commits
77b7772062
...
e236a25da2
Author | SHA1 | Date | |
---|---|---|---|
e236a25da2 | |||
6e41f85996 |
5 changed files with 55 additions and 27 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ config.js
|
||||||
registration.yaml
|
registration.yaml
|
||||||
coverage
|
coverage
|
||||||
db/ooye.db*
|
db/ooye.db*
|
||||||
|
test/res/butterfly*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const assert = require("assert").strict
|
const assert = require("assert").strict
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {_convertImageStream} = require("./emoji-sheet")
|
const {_convertImageStream} = require("./emoji-sheet")
|
||||||
const fetch = require("node-fetch")
|
const fs = require("fs")
|
||||||
const {Transform} = require("stream").Transform
|
const {Transform} = require("stream").Transform
|
||||||
|
|
||||||
/* c8 ignore next 7 */
|
/* c8 ignore next 7 */
|
||||||
|
@ -25,18 +25,16 @@ class Meter extends Transform {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("supertape").Test} t
|
* @param {import("supertape").Test} t
|
||||||
* @param {string} url
|
* @param {string} path
|
||||||
* @param {number} totalSize
|
* @param {number} totalSize
|
||||||
*/
|
*/
|
||||||
async function runSingleTest(t, url, totalSize) {
|
async function runSingleTest(t, path, totalSize) {
|
||||||
const abortController = new AbortController()
|
const file = fs.createReadStream(path)
|
||||||
const res = await fetch("https://ezgif.com/images/format-demo/butterfly.png", {agent: false, signal: abortController.signal})
|
|
||||||
const meter = new Meter()
|
const meter = new Meter()
|
||||||
const p = res.body.pipe(meter)
|
const p = file.pipe(meter)
|
||||||
const result = await _convertImageStream(p, () => {
|
const result = await _convertImageStream(p, () => {
|
||||||
abortController.abort()
|
file.pause()
|
||||||
res.body.pause()
|
file.emit("end")
|
||||||
res.body.emit("end")
|
|
||||||
})
|
})
|
||||||
t.equal(result.subarray(1, 4).toString("ascii"), "PNG", `result was not a PNG file: ${result.toString("base64")}`)
|
t.equal(result.subarray(1, 4).toString("ascii"), "PNG", `result was not a PNG file: ${result.toString("base64")}`)
|
||||||
/* c8 ignore next 5 */
|
/* c8 ignore next 5 */
|
||||||
|
@ -48,9 +46,9 @@ async function runSingleTest(t, url, totalSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
slow()("emoji-sheet: only partial file is read for APNG", async t => {
|
slow()("emoji-sheet: only partial file is read for APNG", async t => {
|
||||||
await runSingleTest(t, "https://ezgif.com/images/format-demo/butterfly.png", 2438998)
|
await runSingleTest(t, "test/res/butterfly.png", 2438998)
|
||||||
})
|
})
|
||||||
|
|
||||||
slow()("emoji-sheet: only partial file is read for GIF", async t => {
|
slow()("emoji-sheet: only partial file is read for GIF", async t => {
|
||||||
await runSingleTest(t, "https://ezgif.com/images/format-demo/butterfly.gif", 781223)
|
await runSingleTest(t, "test/res/butterfly.gif", 781223)
|
||||||
})
|
})
|
||||||
|
|
|
@ -209,14 +209,19 @@ function convertEmoji(mxcUrl, nameForGuess, allowSpriteSheetIndicator, allowLink
|
||||||
// Get the known emoji from the database.
|
// Get the known emoji from the database.
|
||||||
let row
|
let row
|
||||||
if (mxcUrl) row = select("emoji", ["emoji_id", "name", "animated"], {mxc_url: mxcUrl}).get()
|
if (mxcUrl) row = select("emoji", ["emoji_id", "name", "animated"], {mxc_url: mxcUrl}).get()
|
||||||
|
// Now we have to search all servers to see if we're able to send this emoji.
|
||||||
|
if (row) {
|
||||||
|
const found = [...discord.guilds.values()].find(g => g.emojis.find(e => e.id === row.id))
|
||||||
|
if (!found) row = null
|
||||||
|
}
|
||||||
|
// Or, if we don't have an emoji right now, we search for the name instead.
|
||||||
if (!row && nameForGuess) {
|
if (!row && nameForGuess) {
|
||||||
// We don't know the emoji, but we could guess a suitable emoji based on the name
|
|
||||||
const nameForGuessLower = nameForGuess.toLowerCase()
|
const nameForGuessLower = nameForGuess.toLowerCase()
|
||||||
for (const guild of discord.guilds.values()) {
|
for (const guild of discord.guilds.values()) {
|
||||||
/** @type {{name: string, id: string, animated: number}[]} */
|
/** @type {{name: string, id: string, animated: number}[]} */
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const emojis = guild.emojis
|
const emojis = guild.emojis
|
||||||
const found = emojis.find(e => e.name?.toLowerCase() === nameForGuessLower)
|
const found = emojis.find(e => e.id === row?.id || e.name?.toLowerCase() === nameForGuessLower)
|
||||||
if (found) {
|
if (found) {
|
||||||
row = {
|
row = {
|
||||||
animated: found.animated,
|
animated: found.animated,
|
||||||
|
@ -643,19 +648,6 @@ async function eventToMessage(event, guild, di) {
|
||||||
// input = input.replace(/ /g, " ")
|
// input = input.replace(/ /g, " ")
|
||||||
// There is also a corresponding test to uncomment, named "event2message: whitespace is retained"
|
// There is also a corresponding test to uncomment, named "event2message: whitespace is retained"
|
||||||
|
|
||||||
// SPRITE SHEET EMOJIS FEATURE: Emojis at the end of the message that we don't know about will be reuploaded as a sprite sheet.
|
|
||||||
// First we need to determine which emojis are at the end.
|
|
||||||
endOfMessageEmojis = []
|
|
||||||
let match
|
|
||||||
let last = input.length
|
|
||||||
while ((match = input.slice(0, last).match(/<img [^>]*>\s*$/))) {
|
|
||||||
if (!match[0].includes("data-mx-emoticon")) break
|
|
||||||
const mxcUrl = match[0].match(/\bsrc="(mxc:\/\/[^"]+)"/)
|
|
||||||
if (mxcUrl) endOfMessageEmojis.unshift(mxcUrl[1])
|
|
||||||
assert(typeof match.index === "number", "Your JavaScript implementation does not comply with TC39: https://tc39.es/ecma262/multipage/text-processing.html#sec-regexpbuiltinexec")
|
|
||||||
last = match.index
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling written @mentions: we need to look for candidate Discord members to join to the room
|
// Handling written @mentions: we need to look for candidate Discord members to join to the room
|
||||||
// This shouldn't apply to code blocks, links, or inside attributes. So editing the HTML tree instead of regular expressions is a sensible choice here.
|
// This shouldn't apply to code blocks, links, or inside attributes. So editing the HTML tree instead of regular expressions is a sensible choice here.
|
||||||
// We're using the domino parser because Turndown uses the same and can reuse this tree.
|
// We're using the domino parser because Turndown uses the same and can reuse this tree.
|
||||||
|
@ -701,6 +693,19 @@ async function eventToMessage(event, guild, di) {
|
||||||
}
|
}
|
||||||
await forEachNode(root)
|
await forEachNode(root)
|
||||||
|
|
||||||
|
// SPRITE SHEET EMOJIS FEATURE: Emojis at the end of the message that we don't know about will be reuploaded as a sprite sheet.
|
||||||
|
// First we need to determine which emojis are at the end.
|
||||||
|
endOfMessageEmojis = []
|
||||||
|
let match
|
||||||
|
let last = input.length
|
||||||
|
while ((match = input.slice(0, last).match(/<img [^>]*>\s*$/))) {
|
||||||
|
if (!match[0].includes("data-mx-emoticon")) break
|
||||||
|
const mxcUrl = match[0].match(/\bsrc="(mxc:\/\/[^"]+)"/)
|
||||||
|
if (mxcUrl) endOfMessageEmojis.unshift(mxcUrl[1])
|
||||||
|
assert(typeof match.index === "number", "Your JavaScript implementation does not comply with TC39: https://tc39.es/ecma262/multipage/text-processing.html#sec-regexpbuiltinexec")
|
||||||
|
last = match.index
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore bad type from turndown
|
// @ts-ignore bad type from turndown
|
||||||
content = turndownService.turndown(root)
|
content = turndownService.turndown(root)
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"addbot": "node addbot.js",
|
"addbot": "node addbot.js",
|
||||||
"test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot",
|
"test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot",
|
||||||
"test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js -- --slow | tap-dot",
|
"test-slow": "cross-env FORCE_COLOR=true SUPERTAPE_TIMEOUT=6000 supertape --no-check-assertions-count --format tap test/test.js -- --slow | tap-dot",
|
||||||
"cover": "c8 --skip-full -x db/migrations -x matrix/file.js -x matrix/api.js -x matrix/mreq.js -r html -r text supertape --no-check-assertions-count --format fail test/test.js -- --slow"
|
"cover": "c8 --skip-full -x db/migrations -x matrix/file.js -x matrix/api.js -x matrix/mreq.js -r html -r text supertape --no-check-assertions-count --format fail test/test.js -- --slow"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
test/test.js
24
test/test.js
|
@ -7,6 +7,9 @@ const migrate = require("../db/migrate")
|
||||||
const HeatSync = require("heatsync")
|
const HeatSync = require("heatsync")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const data = require("./data")
|
const data = require("./data")
|
||||||
|
/** @type {import("node-fetch").default} */
|
||||||
|
// @ts-ignore
|
||||||
|
const fetch = require("node-fetch")
|
||||||
|
|
||||||
const config = require("../config")
|
const config = require("../config")
|
||||||
const passthrough = require("../passthrough")
|
const passthrough = require("../passthrough")
|
||||||
|
@ -60,6 +63,27 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not
|
||||||
})
|
})
|
||||||
|
|
||||||
db.exec(fs.readFileSync(join(__dirname, "ooye-test-data.sql"), "utf8"))
|
db.exec(fs.readFileSync(join(__dirname, "ooye-test-data.sql"), "utf8"))
|
||||||
|
|
||||||
|
/* c8 ignore start - maybe download some more test files in slow mode */
|
||||||
|
if (process.argv.includes("--slow")) {
|
||||||
|
test("test files: download", async t => {
|
||||||
|
function download(url, to) {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
if (fs.existsSync(to)) return resolve(null)
|
||||||
|
const res = await fetch(url)
|
||||||
|
res.body.pipe(fs.createWriteStream(to, {encoding: "binary"}))
|
||||||
|
res.body.once("finish", resolve)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await Promise.all([
|
||||||
|
download("https://ezgif.com/images/format-demo/butterfly.png", "test/res/butterfly.png"),
|
||||||
|
download("https://ezgif.com/images/format-demo/butterfly.gif", "test/res/butterfly.gif")
|
||||||
|
])
|
||||||
|
t.pass("downloaded")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* c8 ignore end */
|
||||||
|
|
||||||
require("../db/orm.test")
|
require("../db/orm.test")
|
||||||
require("../discord/utils.test")
|
require("../discord/utils.test")
|
||||||
require("../matrix/kstate.test")
|
require("../matrix/kstate.test")
|
||||||
|
|
Loading…
Reference in a new issue