Skip to content

Commit e28cab0

Browse files
authored
Fix legacy user context menu loading (#632)
1 parent 00b6fb7 commit e28cab0

6 files changed

Lines changed: 193 additions & 4 deletions

File tree

‎packages/legacy/package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
],
1010
"scripts": {
1111
"check-types": "tsc --noEmit",
12-
"build": "tsc"
12+
"build": "tsc",
13+
"test": "vitest"
1314
},
1415
"repository": {
1516
"type": "git",
@@ -33,6 +34,7 @@
3334
"commandkit": "workspace:*",
3435
"discord.js": "catalog:discordjs",
3536
"tsconfig": "workspace:*",
36-
"typescript": "catalog:build"
37+
"typescript": "catalog:build",
38+
"vitest": "^4.0.18"
3739
}
3840
}

‎packages/legacy/src/plugin.test.ts‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { afterEach, describe, expect, test } from 'vitest';
2+
import { ApplicationCommandType } from 'discord.js';
3+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
4+
import { join, relative } from 'node:path';
5+
import { LegacyHandlerPlugin } from './plugin';
6+
import { loadLegacyCommands } from './loadLegacyCommands';
7+
import { CommandRegistrar, type LoadedCommand } from 'commandkit';
8+
9+
const tmpRoots: string[] = [];
10+
const commandkitBaseDir = join(process.cwd(), 'dist');
11+
const tempBaseDir = join(commandkitBaseDir, '.tmp');
12+
13+
async function createLegacyCommandFixture(
14+
fileName: string,
15+
type: ApplicationCommandType,
16+
) {
17+
await mkdir(tempBaseDir, { recursive: true });
18+
const root = await mkdtemp(join(tempBaseDir, 'legacy-commands-'));
19+
tmpRoots.push(root);
20+
21+
const filePath = join(root, fileName);
22+
await writeFile(
23+
filePath,
24+
`
25+
import { ApplicationCommandType } from 'discord.js';
26+
27+
export const data = {
28+
name: ${JSON.stringify(fileName.replace(/\.[^.]+$/, ''))},
29+
type: ApplicationCommandType.${type === ApplicationCommandType.User ? 'User' : 'Message'},
30+
};
31+
32+
export async function run() {
33+
return ${JSON.stringify(type === ApplicationCommandType.User ? 'user' : 'message')};
34+
}
35+
`,
36+
);
37+
38+
return root;
39+
}
40+
41+
async function loadPluginCommands(commandsRoot: string) {
42+
const registered: LoadedCommand[] = [];
43+
const plugin = new LegacyHandlerPlugin({
44+
commandsPath: relative(commandkitBaseDir, commandsRoot).replace(/\\/g, '/'),
45+
eventsPath: './events',
46+
validationsPath: './validations',
47+
skipBuiltInValidations: true,
48+
devUserIds: [],
49+
devGuildIds: [],
50+
devRoleIds: [],
51+
});
52+
53+
await (plugin as any).loadCommands(
54+
{
55+
commandkit: {
56+
commandHandler: {
57+
registerExternalLoadedCommands: async (commands: LoadedCommand[]) => {
58+
registered.push(...commands);
59+
},
60+
},
61+
},
62+
},
63+
[],
64+
);
65+
66+
return registered;
67+
}
68+
69+
function createRegistrarData(commands: LoadedCommand[]) {
70+
const registrar = new CommandRegistrar({
71+
client: {
72+
token: 'test-token',
73+
},
74+
commandHandler: {
75+
getCommandsArray: () => commands,
76+
},
77+
commandsRouter: null,
78+
plugins: {
79+
execute: async () => undefined,
80+
},
81+
} as any);
82+
83+
return registrar.getCommandsData();
84+
}
85+
86+
afterEach(async () => {
87+
await Promise.all(
88+
tmpRoots
89+
.splice(0)
90+
.map((root) => rm(root, { recursive: true, force: true })),
91+
);
92+
});
93+
94+
describe('legacy context menu command loading', () => {
95+
test('maps legacy message context menu commands to modern loaded commands', async () => {
96+
const root = await createLegacyCommandFixture(
97+
'inspect-message.mjs',
98+
ApplicationCommandType.Message,
99+
);
100+
101+
const [legacyCommand] = await loadLegacyCommands(root);
102+
const [loadedCommand] = await loadPluginCommands(root);
103+
104+
expect(legacyCommand.messageContextMenu).toBeTypeOf('function');
105+
expect(legacyCommand.userContextMenu).toBeUndefined();
106+
expect(loadedCommand.data.messageContextMenu).toBeTypeOf('function');
107+
expect(loadedCommand.data.userContextMenu).toBeUndefined();
108+
});
109+
110+
test('maps legacy user context menu commands to modern loaded commands', async () => {
111+
const root = await createLegacyCommandFixture(
112+
'inspect-user.mjs',
113+
ApplicationCommandType.User,
114+
);
115+
116+
const [legacyCommand] = await loadLegacyCommands(root);
117+
const [loadedCommand] = await loadPluginCommands(root);
118+
119+
expect(legacyCommand.userContextMenu).toBeTypeOf('function');
120+
expect(legacyCommand.messageContextMenu).toBeUndefined();
121+
expect(loadedCommand.data.userContextMenu).toBeTypeOf('function');
122+
expect(loadedCommand.data.messageContextMenu).toBeUndefined();
123+
});
124+
125+
test('emits user context menu registration data for legacy loaded commands', async () => {
126+
const root = await createLegacyCommandFixture(
127+
'inspect-user.mjs',
128+
ApplicationCommandType.User,
129+
);
130+
131+
const [legacyCommand] = await loadLegacyCommands(root);
132+
const [loadedCommand] = await loadPluginCommands(root);
133+
const registrationCommands = createRegistrarData([loadedCommand]);
134+
135+
expect(registrationCommands).toHaveLength(1);
136+
expect(registrationCommands[0]).toMatchObject({
137+
name: 'inspect-user',
138+
type: ApplicationCommandType.User,
139+
});
140+
expect(registrationCommands[0].description).toBeUndefined();
141+
expect(registrationCommands[0].options).toBeUndefined();
142+
});
143+
144+
test('executes the wrapped legacy user context menu runner', async () => {
145+
const root = await createLegacyCommandFixture(
146+
'inspect-user.mjs',
147+
ApplicationCommandType.User,
148+
);
149+
150+
const [legacyCommand] = await loadLegacyCommands(root);
151+
const [loadedCommand] = await loadPluginCommands(root);
152+
const result = await loadedCommand.data.userContextMenu?.({
153+
client: {},
154+
interaction: {},
155+
commandkit: {},
156+
} as any);
157+
158+
expect(result).toBe('user');
159+
});
160+
});

‎packages/legacy/src/plugin.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export class LegacyHandlerPlugin extends RuntimePlugin<LegacyHandlerPluginOption
192192
handler: ctx.commandkit,
193193
})
194194
: undefined,
195-
userContextMenu: command.messageContextMenu
195+
userContextMenu: command.userContextMenu
196196
? (ctx: UserContextMenuCommandContext) =>
197197
command.userContextMenu?.({
198198
client: ctx.client as any,

‎packages/legacy/tsconfig.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"noEmit": false
1515
},
1616
"include": ["src/**/*.ts"],
17-
"exclude": ["node_modules"]
17+
"exclude": ["node_modules", "src/**/*.test.ts", "src/**/*.spec.ts"]
1818
}

‎packages/legacy/vitest.config.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { defineConfig } from 'vitest/config';
2+
import { join } from 'node:path';
3+
4+
export default defineConfig({
5+
test: {
6+
include: ['./src/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
7+
exclude: ['dist/**', 'node_modules/**'],
8+
watch: false,
9+
env: {
10+
COMMANDKIT_TEST: 'true',
11+
},
12+
},
13+
resolve: {
14+
alias: {
15+
commandkit: join(
16+
import.meta.dirname,
17+
'..',
18+
'commandkit',
19+
'src',
20+
'index.ts',
21+
),
22+
},
23+
},
24+
});

‎pnpm-lock.yaml‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)