Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions src/m365/teams/commands/channel/channel-member-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ describe(commands.CHANNEL_MEMBER_ADD, () => {
]
};

const singleSharedChannelResponse: any = {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('47d6625d-a540-4b59-a4ab-19b787e40593')/channels",
"@odata.count": 1,
"value": [
{
"id": "19:[email protected]",
"displayName": "Shared Channel",
"membershipType": "shared"
}
]
};

const channelIdResponse: any = {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('47d6625d-a540-4b59-a4ab-19b787e40593')/channels/$entity",
"id": "19:[email protected]",
Expand Down Expand Up @@ -225,6 +237,10 @@ describe(commands.CHANNEL_MEMBER_ADD, () => {
return singleChannelResponse;
}

if (opts.url === `https://graph.microsoft.com/v1.0/teams/${formatting.encodeQueryParameter('47d6625d-a540-4b59-a4ab-19b787e40593')}/channels?$filter=displayName eq '${formatting.encodeQueryParameter('Shared Channel')}'`) {
return singleSharedChannelResponse;
}

throw 'Invalid request';
});
log = [];
Expand Down Expand Up @@ -443,6 +459,27 @@ describe(commands.CHANNEL_MEMBER_ADD, () => {
assert(loggerLogSpy.notCalled);
});

it('adds conversation members using teamId, shared channelName, and userIds', async () => {
sinonUtil.restore(request.post);
sinon.stub(request, 'post').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/teams/${formatting.encodeQueryParameter('47d6625d-a540-4b59-a4ab-19b787e40593')}/channels/${formatting.encodeQueryParameter('19:[email protected]')}/members`) {
return conversationMembersOwnerResponse;
}

throw 'Invalid request';
});

await command.action(logger, {
options: {
teamId: "47d6625d-a540-4b59-a4ab-19b787e40593",
channelName: "Shared Channel",
userIds: "[email protected]",
owner: true
}
});
assert(loggerLogSpy.notCalled);
});

it('fails adding conversation members with invalid channelName', async () => {
sinonUtil.restore(request.get);
sinon.stub(request, 'get').callsFake(async (opts) => {
Expand All @@ -469,7 +506,7 @@ describe(commands.CHANNEL_MEMBER_ADD, () => {
} as any), new CommandError(`The specified channel 'Other Private Channel' does not exist in the Microsoft Teams team with ID '47d6625d-a540-4b59-a4ab-19b787e40593'`));
});

it('fails to get channel when channel does is not private', async () => {
it('fails to get channel when channel is not private or shared', async () => {
sinonUtil.restore(request.get);
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter('Human Resources')}'`) {
Expand All @@ -495,7 +532,7 @@ describe(commands.CHANNEL_MEMBER_ADD, () => {
teamId: "47d6625d-a540-4b59-a4ab-19b787e40593",
channelName: "Other Channel"
}
} as any), new CommandError('The specified channel is not a private channel'));
} as any), new CommandError('The specified channel is not a private or shared channel'));
});

it('fails when group has no team', async () => {
Expand Down
6 changes: 3 additions & 3 deletions src/m365/teams/commands/channel/channel-member-add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ class TeamsChannelMemberAddCommand extends GraphCommand {
throw `The specified channel '${args.options.channelName}' does not exist in the Microsoft Teams team with ID '${teamId}'`;
}

if (channelItem.membershipType !== "private") {
throw `The specified channel is not a private channel`;
if (channelItem.membershipType !== 'private' && channelItem.membershipType !== 'shared') {
throw 'The specified channel is not a private or shared channel';
Comment on lines +183 to +184
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this fix correctly adds support for shared channels to teams channel member add, there are related commands teams channel member remove (src/m365/teams/commands/channel/channel-member-remove.ts:210-211) and teams channel member set (src/m365/teams/commands/channel/channel-member-set.ts:193-194) that have the same limitation. Their descriptions already mention "private or shared team channel" but their validation logic still only accepts private channels. Consider updating those commands as well to maintain consistency and ensure all channel member management commands fully support shared channels.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@milanholemans milanholemans Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid point @adamlonsdale. Could you have a look at the other commands as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fair point, I was weighing up the tradeoff between multiple commits / PRs vs this one. I'll add these now, test and push an update

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the fix is not that large, I think we can do them in one PR.

}

return channelItem.id!;
Expand Down Expand Up @@ -228,4 +228,4 @@ class TeamsChannelMemberAddCommand extends GraphCommand {
}
}

export default new TeamsChannelMemberAddCommand();
export default new TeamsChannelMemberAddCommand();