test container instead of render

allows awaiting renders
This commit is contained in:
MapleLeaf
2021-12-09 04:54:22 -06:00
parent a46c214cdf
commit 0806e077b7
3 changed files with 105 additions and 120 deletions

81
src/container.test.ts Normal file
View File

@@ -0,0 +1,81 @@
import test from "ava"
import { Client, TextChannel } from "discord.js"
import { nanoid } from "nanoid"
import { ReacordContainer } from "./container.js"
import { raise } from "./helpers/raise.js"
import { testBotToken, testChannelId } from "./test-environment.js"
const client = new Client({
intents: ["GUILDS"],
})
let channel: TextChannel
test.before(async () => {
await client.login(testBotToken)
const result =
client.channels.cache.get(testChannelId) ??
(await client.channels.fetch(testChannelId)) ??
raise("Channel not found")
if (!(result instanceof TextChannel)) {
throw new TypeError("Channel must be a text channel")
}
channel = result
})
test.after(() => {
client.destroy()
})
test("rendering text", async (t) => {
const container = new ReacordContainer(channel)
const content = nanoid()
await container.render([content])
{
const messages = await channel.messages.fetch()
t.true(messages.some((m) => m.content === content))
}
const newContent = nanoid()
await container.render([newContent])
{
const messages = await channel.messages.fetch()
t.true(messages.some((m) => m.content === newContent))
}
await container.render([])
{
const messages = await channel.messages.fetch()
t.false(messages.some((m) => m.content === newContent))
}
})
test("rapid updates", async (t) => {
const container = new ReacordContainer(channel)
const content = nanoid()
const newContent = nanoid()
void container.render([content])
await container.render([newContent])
{
const messages = await channel.messages.fetch()
t.true(messages.some((m) => m.content === newContent))
}
void container.render([content])
await container.render([])
{
const messages = await channel.messages.fetch()
t.false(messages.some((m) => m.content === newContent))
}
})

View File

@@ -1,4 +1,5 @@
import type { Message, MessageOptions, TextBasedChannels } from "discord.js"
import { createDeferred } from "./helpers/deferred.js"
type Action =
| { type: "updateMessage"; options: MessageOptions }
@@ -8,38 +9,42 @@ export class ReacordContainer {
channel: TextBasedChannels
message?: Message
actions: Action[] = []
runningActions = false
runningPromise?: PromiseLike<void>
constructor(channel: TextBasedChannels) {
this.channel = channel
}
render(instances: string[]) {
async render(instances: string[]) {
const messageOptions: MessageOptions = {
content: instances.join("") || undefined, // empty strings are not allowed
}
const hasContent = messageOptions.content !== undefined
if (hasContent) {
this.addAction({ type: "updateMessage", options: messageOptions })
} else {
this.addAction({ type: "deleteMessage" })
}
await this.addAction(
hasContent
? { type: "updateMessage", options: messageOptions }
: { type: "deleteMessage" },
)
}
private addAction(action: Action) {
private async addAction(action: Action) {
const lastAction = this.actions[this.actions.length - 1]
if (lastAction?.type === action.type) {
this.actions[this.actions.length - 1] = action
} else {
this.actions.push(action)
}
void this.runActions()
await this.runActions()
}
private runActions() {
if (this.runningActions) return
this.runningActions = true
private async runActions() {
if (this.runningPromise) {
return this.runningPromise
}
const promise = (this.runningPromise = createDeferred())
queueMicrotask(async () => {
let action: Action | undefined
@@ -47,11 +52,9 @@ export class ReacordContainer {
try {
switch (action.type) {
case "updateMessage":
if (this.message) {
await this.message.edit(action.options)
} else {
this.message = await this.channel.send(action.options)
}
this.message = await (this.message
? this.message.edit(action.options)
: this.channel.send(action.options))
break
case "deleteMessage":
if (this.message) {
@@ -66,7 +69,10 @@ export class ReacordContainer {
}
}
this.runningActions = false
promise.resolve()
})
await promise
this.runningPromise = undefined
}
}

View File

@@ -1,102 +0,0 @@
import test from "ava"
import { Client, TextChannel } from "discord.js"
import { nanoid } from "nanoid"
import { raise } from "./helpers/raise.js"
import { waitForWithTimeout } from "./helpers/wait-for-with-timeout.js"
import { render } from "./render.js"
import { testBotToken, testChannelId } from "./test-environment.js"
const client = new Client({
intents: ["GUILDS"],
})
let channel: TextChannel
test.before(async () => {
await client.login(testBotToken)
const result =
client.channels.cache.get(testChannelId) ??
(await client.channels.fetch(testChannelId)) ??
raise("Channel not found")
if (!(result instanceof TextChannel)) {
throw new TypeError("Channel must be a text channel")
}
channel = result
})
test.after(() => {
client.destroy()
})
test.serial("rendering text", async (t) => {
const content = nanoid()
const root = render(content, channel)
await waitForWithTimeout(
async () => {
const messages = await channel.messages.fetch()
return messages.some((m) => m.content === content)
},
10_000,
"Message not found",
)
const newContent = nanoid()
root.rerender(newContent)
await waitForWithTimeout(
async () => {
const messages = await channel.messages.fetch()
return messages.some((m) => m.content === newContent)
},
10_000,
"Message not found",
)
root.destroy()
await waitForWithTimeout(
async () => {
const messages = await channel.messages.fetch()
return messages.size === 0
},
10_000,
"Message was not deleted",
)
t.pass()
})
test.serial("rapid updates", async (t) => {
const content = nanoid()
const newContent = nanoid()
const root = render(content, channel)
root.rerender(newContent)
await waitForWithTimeout(
async () => {
const messages = await channel.messages.fetch()
return messages.some((m) => m.content === newContent)
},
10_000,
"Message not found",
)
root.rerender(content)
root.destroy()
await waitForWithTimeout(
async () => {
const messages = await channel.messages.fetch()
return messages.size === 0
},
10_000,
"Message was not deleted",
)
t.pass()
})