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