diff --git a/src/discord/interactions/invite.test.js b/src/discord/interactions/invite.test.js
index 8290718..571623d 100644
--- a/src/discord/interactions/invite.test.js
+++ b/src/discord/interactions/invite.test.js
@@ -59,7 +59,7 @@ test("invite: checks if guild exists", async t => { // it might not exist if the
})
test("invite: checks if channel exists or is autocreatable", async t => {
- db.prepare("UPDATE guild_active SET autocreate = 0").run()
+ db.prepare("UPDATE guild_active SET autocreate = 0 WHERE guild_id = '112760669178241024'").run()
const msgs = await fromAsync(_interact({
data: {
options: [{
@@ -72,7 +72,7 @@ test("invite: checks if channel exists or is autocreatable", async t => {
guild_id: "112760669178241024"
}, {}))
t.equal(msgs[0].createInteractionResponse.data.content, "This channel isn't bridged, so you can't invite Matrix users yet. Try turning on automatic room-creation or link a Matrix room in the website.")
- db.prepare("UPDATE guild_active SET autocreate = 1").run()
+ db.prepare("UPDATE guild_active SET autocreate = 1 WHERE guild_id = '112760669178241024'").run()
})
test("invite: checks if user is already invited to space", async t => {
diff --git a/src/web/pug-sync.js b/src/web/pug-sync.js
index fb83caa..a966f06 100644
--- a/src/web/pug-sync.js
+++ b/src/web/pug-sync.js
@@ -7,6 +7,7 @@ const getRelativePath = require("get-relative-path")
const h3 = require("h3")
const {defineEventHandler, defaultContentType, setResponseStatus, useSession, getQuery} = h3
const {compileFile} = require("@cloudrac3r/pug")
+const pretty = process.argv.join(" ").includes("test")
const {reg} = require("../matrix/read-registration")
@@ -31,7 +32,7 @@ function render(event, filename, locals) {
function compile() {
try {
- const template = compileFile(path, {})
+ const template = compileFile(path, {pretty})
pugCache.set(path, async (event, locals) => {
defaultContentType(event, "text/html; charset=utf-8")
const session = await useSession(event, {password: reg.as_token})
diff --git a/src/web/pug/guild_access_denied.pug b/src/web/pug/guild_access_denied.pug
index 319d4de..6e88e81 100644
--- a/src/web/pug/guild_access_denied.pug
+++ b/src/web/pug/guild_access_denied.pug
@@ -23,3 +23,13 @@ block body
!= icons.Spots.SpotAlertXL
p Either the selected server doesn't exist, or you don't have the Manage Server permission on Discord.
p If you've checked your permissions, try #[a(href=rel("/oauth")) logging in again.]
+
+ else if !row
+ .s-empty-state.wmx4.p48
+ != icons.Spots.SpotAlertXL
+ p Please add the bot to your server using the buttons on the home page.
+
+ else
+ .s-empty-state.wmx4.p48
+ != icons.Spots.SpotAlertXL
+ p Access denied.
diff --git a/src/web/pug/guild_not_linked.pug b/src/web/pug/guild_not_linked.pug
index 2eade34..0e2a2d4 100644
--- a/src/web/pug/guild_not_linked.pug
+++ b/src/web/pug/guild_not_linked.pug
@@ -11,7 +11,7 @@ mixin space(space)
strong= space.name
if space.topic
ul.s-user-card--awards
- li space.topic
+ li= space.topic
block body
.s-notice.s-notice__info.d-flex.g16
diff --git a/src/web/routes/guild.js b/src/web/routes/guild.js
index ff645a8..a1bffef 100644
--- a/src/web/routes/guild.js
+++ b/src/web/routes/guild.js
@@ -115,12 +115,12 @@ as.router.get("/guild", defineEventHandler(async event => {
// Permission problems
if (!guild_id || !guild || !(session.data.managedGuilds || []).concat(session.data.matrixGuilds || []).includes(guild_id) || !row) {
- return pugSync.render(event, "guild_access_denied.pug", {guild_id})
+ return pugSync.render(event, "guild_access_denied.pug", {guild_id, row})
}
// Self-service guild that hasn't been linked yet - needs a special page encouraging the link flow
if (!row.space_id && row.autocreate === 0) {
- const spaces = db.prepare("SELECT room_id, type, name, avatar FROM invite LEFT JOIN guild_space ON invite.room_id = guild_space.space_id WHERE mxid = ? AND space_id IS NULL and type = 'm.space'").all(session.data.mxid)
+ const spaces = db.prepare("SELECT room_id, type, name, topic, avatar FROM invite LEFT JOIN guild_space ON invite.room_id = guild_space.space_id WHERE mxid = ? AND space_id IS NULL and type = 'm.space'").all(session.data.mxid)
return pugSync.render(event, "guild_not_linked.pug", {guild, guild_id, spaces})
}
diff --git a/src/web/routes/guild.test.js b/src/web/routes/guild.test.js
index 3ef177e..671baed 100644
--- a/src/web/routes/guild.test.js
+++ b/src/web/routes/guild.test.js
@@ -1,70 +1,96 @@
// @ts-check
const tryToCatch = require("try-to-catch")
-const {test} = require("supertape")
-const {router} = require("../../../test/web")
+const {router, test} = require("../../../test/web")
const {MatrixServerError} = require("../../matrix/mreq")
let nonce
test("web guild: access denied when not logged in", async t => {
- const content = await router.test("get", "/guild?guild_id=112760669178241024", {
+ const html = await router.test("get", "/guild?guild_id=112760669178241024", {
sessionData: {
},
})
- t.match(content, /You need to log in to manage your servers./)
+ t.match(html, /You need to log in to manage your servers./)
})
test("web guild: asks to select guild if not selected", async t => {
- const content = await router.test("get", "/guild", {
+ const html = await router.test("get", "/guild", {
sessionData: {
user_id: "1",
managedGuilds: []
},
})
- t.match(content, /Select a server from the top right corner to continue./)
+ t.match(html, /Select a server from the top right corner to continue./)
})
test("web guild: access denied when guild id messed up", async t => {
- const content = await router.test("get", "/guild?guild_id=1", {
+ const html = await router.test("get", "/guild?guild_id=1", {
sessionData: {
user_id: "1",
managedGuilds: []
},
})
- t.match(content, /the selected server doesn't exist/)
+ t.match(html, /the selected server doesn't exist/)
})
test("web invite: access denied with invalid nonce", async t => {
- const content = await router.test("get", "/invite?nonce=1")
- t.match(content, /This QR code has expired./)
+ const html = await router.test("get", "/invite?nonce=1")
+ t.match(html, /This QR code has expired./)
})
test("web guild: can view unbridged guild", async t => {
- const content = await router.test("get", "/guild?guild_id=66192955777486848", {
+ const html = await router.test("get", "/guild?guild_id=66192955777486848", {
sessionData: {
user_id: "1",
managedGuilds: ["66192955777486848"]
- },
- api: {
- async getStateEvent(roomID, type, key) {
- return {}
- },
- async getMembers(roomID, membership) {
- return {chunk: []}
- },
- async getFullHierarchy(roomID) {
- return []
- }
}
})
- t.match(content, /
`)
})
+test("web guild: unbridged self-service guild prompts log in to matrix", async t => {
+ const html = await router.test("get", "/guild?guild_id=665289423482519565", {
+ sessionData: {
+ user_id: "1",
+ managedGuilds: ["665289423482519565"]
+ }
+ })
+ t.has(html, `You picked self-service mode`)
+ t.has(html, `You need to log in with Matrix first`)
+})
+
+test("web guild: unbridged self-service guild asks to be invited", async t => {
+ const html = await router.test("get", "/guild?guild_id=665289423482519565", {
+ sessionData: {
+ mxid: "@user:example.org",
+ user_id: "1",
+ managedGuilds: ["665289423482519565"]
+ }
+ })
+ t.has(html, `On Matrix, invite <`)
+})
+
+test("web guild: unbridged self-service guild shows available spaces", async t => {
+ const html = await router.test("get", "/guild?guild_id=665289423482519565", {
+ sessionData: {
+ mxid: "@cadence:cadence.moe",
+ user_id: "1",
+ managedGuilds: ["665289423482519565"]
+ }
+ })
+ t.has(html, `Data Horde`)
+ t.has(html, `here is the space topic`)
+ // t.match(html, /
/)
+ // t.notMatch(html, /some room<\/strong>/)
+ // t.notMatch(html, /somebody else's space<\/strong>/)
+})
+
+
test("web guild: can view bridged guild", async t => {
- const content = await router.test("get", "/guild?guild_id=112760669178241024", {
+ const html = await router.test("get", "/guild?guild_id=112760669178241024", {
sessionData: {
managedGuilds: ["112760669178241024"]
},
@@ -80,14 +106,14 @@ test("web guild: can view bridged guild", async t => {
}
}
})
- t.match(content, / {
- const content = await router.test("get", `/invite?nonce=${nonce}`)
- t.match(content, /Invite a Matrix user/)
+ const html = await router.test("get", `/invite?nonce=${nonce}`)
+ t.match(html, /Invite a Matrix user/)
})
diff --git a/test/data.js b/test/data.js
index b92ae1b..c18f173 100644
--- a/test/data.js
+++ b/test/data.js
@@ -18,6 +18,20 @@ module.exports = {
id: "112760669178241024",
default_thread_rate_limit_per_user: 0,
guild_id: "112760669178241024"
+ },
+ saving_the_world: {
+ type: 0,
+ topic: "Anything and everything archiving/preservation related",
+ rate_limit_per_user: 0,
+ position: 0,
+ permission_overwrites: [],
+ parent_id: "665289423482519566",
+ name: "saving-the-world",
+ last_pin_timestamp: "2021-04-14T18:39:41+00:00",
+ last_message_id: "1335828749479837750",
+ id: "665310973967597573",
+ flags: 0,
+ guild_id: "665289423482519565"
}
},
room: {
@@ -252,6 +266,111 @@ module.exports = {
nsfw: false,
safety_alerts_channel_id: null,
lazy: true
+ },
+ data_horde: {
+ preferred_locale: "en-US",
+ afk_channel_id: null,
+ profile: null,
+ owner_id: "222343226990788609",
+ soundboard_sounds: [],
+ hub_type: null,
+ mfa_level: 0,
+ activity_instances: [],
+ inventory_settings: null,
+ voice_states: [],
+ system_channel_id: "675397790204952636",
+ id: "665289423482519565",
+ member_count: 138,
+ clan: null,
+ default_message_notifications: 1,
+ name: "Data Horde",
+ banner: null,
+ premium_subscription_count: 0,
+ max_stage_video_channel_users: 50,
+ max_members: 500000,
+ incidents_data: null,
+ joined_at: "2020-05-10T02:00:10.646000+00:00",
+ unavailable: false,
+ discovery_splash: null,
+ threads: [],
+ system_channel_flags: 0,
+ safety_alerts_channel_id: null,
+ nsfw: false,
+ nsfw_level: 0,
+ stage_instances: [],
+ large: false,
+ icon: "d7c4bdb35c10f21e475a50fb205d5c32",
+ roles: [
+ {
+ version: 1683238686112,
+ unicode_emoji: null,
+ tags: {},
+ position: 0,
+ permissions: "2221982107557441",
+ name: "@everyone",
+ mentionable: false,
+ managed: false,
+ id: "665289423482519565",
+ icon: null,
+ hoist: false,
+ flags: 0,
+ color: 0
+ },
+ {
+ version: 1683791258594,
+ unicode_emoji: null,
+ tags: {},
+ position: 22,
+ permissions: "7515668211",
+ name: "Founder",
+ mentionable: true,
+ managed: false,
+ id: "665290147377578005",
+ icon: null,
+ hoist: false,
+ flags: 0,
+ color: 1752220
+ },
+ {
+ version: 1683791258580,
+ unicode_emoji: null,
+ tags: {},
+ position: 19,
+ permissions: "6546775617",
+ name: "Gaming Alexandria",
+ mentionable: false,
+ managed: false,
+ id: "684524730274807911",
+ icon: null,
+ hoist: false,
+ flags: 0,
+ color: 15844367
+ }
+ ],
+ description: null,
+ afk_timeout: 300,
+ verification_level: 1,
+ latest_onboarding_question_id: null,
+ guild_scheduled_events: [],
+ rules_channel_id: null,
+ embedded_activities: [],
+ region: "deprecated",
+ vanity_url_code: null,
+ application_id: null,
+ premium_tier: 0,
+ explicit_content_filter: 0,
+ stickers: [],
+ public_updates_channel_id: null,
+ splash: null,
+ premium_progress_bar_enabled: false,
+ features: [],
+ lazy: true,
+ max_video_channel_users: 25,
+ application_command_counts: {},
+ home_header: null,
+ version: 1717720047590,
+ emojis: [],
+ presences: []
}
},
user: {
diff --git a/test/ooye-test-data.sql b/test/ooye-test-data.sql
index 4a7d2f4..ab6c73e 100644
--- a/test/ooye-test-data.sql
+++ b/test/ooye-test-data.sql
@@ -1,7 +1,9 @@
BEGIN TRANSACTION;
INSERT INTO guild_active (guild_id, autocreate) VALUES
-('112760669178241024', 1);
+('112760669178241024', 1),
+('66192955777486848', 1),
+('665289423482519565', 0);
INSERT INTO guild_space (guild_id, space_id, privacy_level) VALUES
('112760669178241024', '!jjWAGMeQdNrVZSSfvz:cadence.moe', 0);
@@ -171,4 +173,9 @@ INSERT INTO media_proxy (permitted_hash) VALUES
(-429802515645771439),
(4558604729745184757);
+INSERT INTO invite (mxid, room_id, type, name, avatar, topic) VALUES
+('@cadence:cadence.moe', '!zTMspHVUBhFLLSdmnS:cadence.moe', 'm.space', 'Data Horde', 'mxc://cadence.moe/TLqQOsTSrZkVKwBSWYTZNTrw', 'here is the space topic'),
+('@cadence:cadence.moe', '!room:cadence.moe', NULL, 'some room', NULL, NULL),
+('@rnl:cadence.moe', '!space:cadence.moe', NULL, 'somebody else''s space', NULL, NULL);
+
COMMIT;
diff --git a/test/test.js b/test/test.js
index 69e3d48..eabab9f 100644
--- a/test/test.js
+++ b/test/test.js
@@ -6,7 +6,7 @@ const stp = require("stream").promises
const sqlite = require("better-sqlite3")
const migrate = require("../src/db/migrate")
const HeatSync = require("heatsync")
-const {test} = require("supertape")
+const {test, extend} = require("supertape")
const data = require("./data")
/** @type {import("node-fetch").default} */
// @ts-ignore
@@ -31,10 +31,12 @@ const discord = {
guilds: new Map([
[data.guild.general.id, data.guild.general],
[data.guild.fna.id, data.guild.fna],
+ [data.guild.data_horde.id, data.guild.data_horde]
]),
guildChannelMap: new Map([
[data.guild.general.id, [data.channel.general.id]],
[data.guild.fna.id, []],
+ [data.guild.data_horde.id, [data.channel.saving_the_world.id]]
]),
application: {
id: "684280192553844747"
@@ -47,7 +49,8 @@ const discord = {
["498323546729086986", {
guild_id: "497159726455455754",
name: "bad-boots-prison"
- }]
+ }],
+ [data.channel.saving_the_world.id, data.channel.saving_the_world]
])
}
diff --git a/test/web.js b/test/web.js
index e22a97c..f13cbf0 100644
--- a/test/web.js
+++ b/test/web.js
@@ -2,6 +2,35 @@ const passthrough = require("../src/passthrough")
const h3 = require("h3")
const http = require("http")
const {SnowTransfer} = require("snowtransfer")
+const assert = require("assert").strict
+const domino = require("domino")
+const {extend} = require("supertape")
+
+/**
+ * @param {string} html
+ */
+function getContent(html) {
+ const doc = domino.createDocument(html)
+ doc.querySelectorAll("svg").cache.forEach(e => e.remove())
+ const content = doc.getElementById("content")
+ assert(content)
+ return content.innerHTML.trim()
+}
+
+const test = extend({
+ has: operator => /** @param {string | RegExp} expected */ (html, expected, message = "should have substring in html content") => {
+ const content = getContent(html)
+ const is = expected instanceof RegExp ? content.match(expected) : content.includes(expected)
+ const {output, result} = operator.equal(content, expected.toString())
+ return {
+ expected: expected.toString(),
+ message,
+ is,
+ result: result,
+ output: output
+ }
+ }
+})
class Router {
constructor() {
@@ -67,3 +96,4 @@ const router = new Router()
passthrough.as = {router}
module.exports.router = router
+module.exports.test = test