Skip to content

Commit 5767984

Browse files
authored
feat: assignees on items (#98)
* feat: assignees on items * fix: handle draft issues with different GraphQL mutation * chore: try getting content ID for replaceActorsForAssignable * chore: some more fixes * chore: add more integration test coverage * chore: improvements * chore: cleanup tests * chore: integration test tweaks * chore: use content ID when editing draft issues * chore: format * chore: more fixes * chore: update integration test * chore: more tweaks to integration test * chore: whoops * chore: back out unnecessary changes * chore: revert test changes * chore: update integration test * chore: make integration test work * chore: whoops * chore: extend integration tests * chore: again * chore: disable test broken by upstream * chore: another integration test fix * chore: fix type checking
1 parent fb18a67 commit 5767984

29 files changed

Lines changed: 712 additions & 97 deletions

.github/workflows/integration-tests.yml

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,47 @@ jobs:
108108
project-number: ${{ steps.copy-project.outputs.number }}
109109
token: ${{ steps.get-auth-token.outputs.token }}
110110

111-
- name: Check Draft Issue
111+
# Upstream needs to fix https://github.com/cli/cli/issues/12726
112+
#
113+
# - name: Edit Draft Issue Title
114+
# uses: ./edit-item/
115+
# id: edit-draft-issue-title
116+
# with:
117+
# item: ${{ steps.get-draft-issue.outputs.id }}
118+
# owner: ${{ steps.copy-project.outputs.owner }}
119+
# project-number: ${{ steps.copy-project.outputs.number }}
120+
# token: ${{ steps.get-auth-token.outputs.token }}
121+
# title: New Draft Issue Title
122+
123+
- name: Edit Draft Issue Assignees
124+
uses: ./edit-item/
125+
id: edit-draft-issue-assignees
126+
with:
127+
assignees: dsanders11
128+
item: ${{ steps.get-draft-issue.outputs.id }}
129+
owner: ${{ steps.copy-project.outputs.owner }}
130+
project-number: ${{ steps.copy-project.outputs.number }}
131+
token: ${{ steps.get-auth-token.outputs.token }}
132+
133+
- name: Get Draft Issue (again)
134+
uses: ./get-item/
135+
id: get-draft-issue-again
136+
with:
137+
field: Priority
138+
item: ${{ steps.get-draft-issue-id.outputs.id }}
139+
owner: ${{ steps.copy-project.outputs.owner }}
140+
project-number: ${{ steps.copy-project.outputs.number }}
141+
token: ${{ steps.get-auth-token.outputs.token }}
142+
143+
- name: Check Draft Issue Assignees
112144
uses: ./github-script/
113145
env:
114-
BODY: ${{ steps.get-draft-issue.outputs.body }}
115-
FIELD_VALUE: ${{ steps.get-draft-issue.outputs.field-value }}
116-
TITLE: ${{ steps.get-draft-issue.outputs.title }}
146+
ASSIGNEES: ${{ steps.get-draft-issue-again.outputs.assignees }}
117147
with:
118148
script: |
119149
const assert = require('node:assert');
120150
121-
assert.strictEqual(process.env.TITLE, 'My Favorite Issue');
122-
assert.strictEqual(process.env.FIELD_VALUE, 'Low');
123-
assert.match(process.env.BODY, /Here's another bar/);
151+
assert.strictEqual(process.env.ASSIGNEES, 'dsanders11');
124152
125153
- name: Run Completed By
126154
uses: ./completed-by/
@@ -177,7 +205,7 @@ jobs:
177205
project-number: ${{ steps.copy-project.outputs.number }}
178206
token: ${{ steps.get-auth-token.outputs.token }}
179207

180-
- name: Check Added Item Field Value
208+
- name: Check Added Item
181209
uses: ./github-script/
182210
env:
183211
DATE_STRING: ${{ steps.get-date.outputs.result }}
@@ -188,6 +216,38 @@ jobs:
188216
189217
assert.strictEqual(process.env.FIELD_VALUE, new Date(process.env.DATE_STRING).toISOString().split('T')[0]);
190218
219+
- name: Edit Item Assignees
220+
uses: ./edit-item/
221+
id: edit-item-assignees
222+
if: ${{ matrix.owner == 'dsanders11-playground-org' && matrix.auth != 'GHA' }}
223+
with:
224+
assignees: dsanders11
225+
item: ${{ steps.get-added-item.outputs.id }}
226+
owner: ${{ steps.copy-project.outputs.owner }}
227+
project-number: ${{ steps.copy-project.outputs.number }}
228+
token: ${{ steps.get-auth-token.outputs.token }}
229+
230+
- name: Get Item After Assignee Edit
231+
uses: ./get-item/
232+
id: get-item-after-assignee-edit
233+
if: ${{ matrix.owner == 'dsanders11-playground-org' && matrix.auth != 'GHA' }}
234+
with:
235+
item: ${{ steps.get-added-item.outputs.id }}
236+
owner: ${{ steps.copy-project.outputs.owner }}
237+
project-number: ${{ steps.copy-project.outputs.number }}
238+
token: ${{ steps.get-auth-token.outputs.token }}
239+
240+
- name: Check Assignees After Edit
241+
uses: ./github-script/
242+
if: ${{ matrix.owner == 'dsanders11-playground-org' && matrix.auth != 'GHA' }}
243+
env:
244+
ASSIGNEES: ${{ steps.get-item-after-assignee-edit.outputs.assignees }}
245+
with:
246+
script: |
247+
const assert = require('node:assert');
248+
249+
assert.strictEqual(process.env.ASSIGNEES, 'dsanders11');
250+
191251
- name: Edit Item
192252
uses: ./edit-item/
193253
id: edit-item
@@ -265,6 +325,37 @@ jobs:
265325
assert.strictEqual(item.field?.value, '⛰️ High');
266326
assert.strictEqual(items.length, 2, 'Expected 2 items');
267327
328+
- name: Test GitHub Script Action (Assignees)
329+
uses: ./github-script/
330+
env:
331+
ITEM_ID: ${{ steps.get-draft-issue.outputs.id }}
332+
PROJECT_ID: ${{ steps.copy-project.outputs.id }}
333+
PROJECT_NUMBER: ${{ steps.copy-project.outputs.number }}
334+
OWNER: ${{ steps.copy-project.outputs.owner }}
335+
with:
336+
token: ${{ steps.get-auth-token.outputs.token }}
337+
script: |
338+
const assert = require('node:assert');
339+
340+
let item = await actions.getItem(
341+
process.env.OWNER,
342+
process.env.PROJECT_NUMBER,
343+
process.env.ITEM_ID,
344+
);
345+
346+
await actions.editItem(process.env.PROJECT_ID, item.content.id, {
347+
assignees: ['dsanders11'],
348+
});
349+
350+
// Verify assignees were set by re-fetching the item
351+
item = await actions.getItem(
352+
process.env.OWNER,
353+
process.env.PROJECT_NUMBER,
354+
process.env.ITEM_ID,
355+
);
356+
const logins = item.content.assignees.nodes.map(a => a.login);
357+
assert.ok(logins.includes('dsanders11'), 'Expected dsanders11 to be an assignee');
358+
268359
# - name: Archive Item
269360
# uses: ./archive-item/
270361
# id: archive-item

__tests__/add-item.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,34 @@ describe('addItemAction', () => {
139139
});
140140
});
141141

142+
it('sets assignees after adding', async () => {
143+
const assigneeLogins = ['octocat', 'dsanders11'];
144+
mockGetInput({
145+
owner,
146+
'project-number': projectNumber,
147+
'content-url': contentUrl,
148+
assignees: assigneeLogins.join(',')
149+
});
150+
vi.mocked(addItem).mockResolvedValue(itemId);
151+
vi.mocked(getProject).mockResolvedValue({
152+
id: projectId
153+
} as ProjectDetails);
154+
vi.mocked(editItem).mockResolvedValue();
155+
156+
await index.addItemAction();
157+
expect(addItemActionSpy).toHaveReturned();
158+
159+
expect(editItem).toHaveBeenCalledWith(
160+
projectId,
161+
itemId,
162+
expect.objectContaining({
163+
assignees: assigneeLogins
164+
})
165+
);
166+
expect(core.setOutput).toHaveBeenCalledTimes(1);
167+
expect(core.setOutput).toHaveBeenLastCalledWith('id', itemId);
168+
});
169+
142170
it('sets output', async () => {
143171
mockGetInput({
144172
owner,

__tests__/completed-by.test.mts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ describe('completedByAction', () => {
139139
content: {
140140
id: 'content-id',
141141
body: 'This is the item body',
142-
title: 'Item Title'
142+
title: 'Item Title',
143+
assignees: { nodes: [] }
143144
},
144145
type: 'DRAFT_ISSUE'
145146
}
@@ -170,7 +171,8 @@ describe('completedByAction', () => {
170171
content: {
171172
id: 'content-id',
172173
body: `Completed by ${itemUrl}`,
173-
title: 'Item Title'
174+
title: 'Item Title',
175+
assignees: { nodes: [] }
174176
},
175177
type: 'DRAFT_ISSUE'
176178
}
@@ -206,7 +208,8 @@ describe('completedByAction', () => {
206208
content: {
207209
id: 'content-id',
208210
body: `Completed by ${itemUrl}`,
209-
title: 'Item Title'
211+
title: 'Item Title',
212+
assignees: { nodes: [] }
210213
},
211214
type: 'DRAFT_ISSUE'
212215
}
@@ -243,7 +246,8 @@ describe('completedByAction', () => {
243246
Completed by ${itemUrl1}
244247
Completed by ${itemUrl2}
245248
`,
246-
title: 'Item Title'
249+
title: 'Item Title',
250+
assignees: { nodes: [] }
247251
},
248252
type: 'DRAFT_ISSUE'
249253
}
@@ -280,7 +284,8 @@ describe('completedByAction', () => {
280284
content: {
281285
id: 'content-id',
282286
body: `Completed by ${itemUrl}`,
283-
title: 'Item Title'
287+
title: 'Item Title',
288+
assignees: { nodes: [] }
284289
},
285290
type: 'DRAFT_ISSUE'
286291
}

__tests__/copy-project.test.mts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ describe('copyProjectAction', () => {
244244
content: {
245245
id: 'content-id',
246246
body: 'This is the item body',
247-
title: 'Item Title'
247+
title: 'Item Title',
248+
assignees: { nodes: [] }
248249
},
249250
type: 'DRAFT_ISSUE'
250251
}
@@ -282,7 +283,8 @@ describe('copyProjectAction', () => {
282283
content: {
283284
id: contentId,
284285
body: 'This is the item {{ foo }}',
285-
title: 'Item {{ foo }}'
286+
title: 'Item {{ foo }}',
287+
assignees: { nodes: [] }
286288
},
287289
type: 'DRAFT_ISSUE'
288290
}
@@ -329,7 +331,8 @@ describe('copyProjectAction', () => {
329331
"${field}": "${fieldValue}"
330332
}
331333
-->`,
332-
title: 'Item Title'
334+
title: 'Item Title',
335+
assignees: { nodes: [] }
333336
},
334337
type: 'DRAFT_ISSUE'
335338
}
@@ -367,7 +370,8 @@ describe('copyProjectAction', () => {
367370
content: {
368371
id: contentId,
369372
body: 'This is the item {{ foo }}',
370-
title: 'Item {{ foo }}'
373+
title: 'Item {{ foo }}',
374+
assignees: { nodes: [] }
371375
},
372376
type: 'DRAFT_ISSUE'
373377
}

__tests__/edit-item.test.mts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('editItemAction', () => {
8787
it('handles item not found', async () => {
8888
mockGetInput({ owner, 'project-number': projectNumber, item });
8989
mockGetBooleanInput({ 'fail-if-item-not-found': true });
90-
vi.mocked(editItem).mockResolvedValue(itemId);
90+
vi.mocked(editItem).mockResolvedValue();
9191

9292
await index.editItemAction();
9393
expect(editItemActionSpy).toHaveReturned();
@@ -206,7 +206,7 @@ describe('editItemAction', () => {
206206
type: 'PULL_REQUEST',
207207
projectId
208208
} as ItemDetails);
209-
vi.mocked(editItem).mockResolvedValue(itemId);
209+
vi.mocked(editItem).mockResolvedValue();
210210

211211
await index.editItemAction();
212212
expect(editItemActionSpy).toHaveReturned();
@@ -220,6 +220,7 @@ describe('editItemAction', () => {
220220
it('can edit draft issue content', async () => {
221221
const title = 'New Title';
222222
const body = 'New Body';
223+
const contentId = 'content-id';
223224
mockGetInput({
224225
owner,
225226
'project-number': projectNumber,
@@ -230,14 +231,54 @@ describe('editItemAction', () => {
230231
vi.mocked(getItem).mockResolvedValue({
231232
id: itemId,
232233
type: 'DRAFT_ISSUE',
233-
projectId
234+
projectId,
235+
content: {
236+
id: contentId
237+
}
234238
} as ItemDetails);
235-
vi.mocked(editItem).mockResolvedValue(itemId);
239+
vi.mocked(editItem).mockResolvedValue();
236240

237241
await index.editItemAction();
238242
expect(editItemActionSpy).toHaveReturned();
239243

240-
expect(editItem).toHaveBeenCalledWith(projectId, itemId, { title, body });
244+
expect(editItem).toHaveBeenCalledWith(projectId, contentId, {
245+
title,
246+
body
247+
});
248+
});
249+
250+
it('can set assignees', async () => {
251+
const assigneeLogins = ['octocat', 'dsanders11'];
252+
const currentAssignees = [{ id: 'old-user-id', login: 'old-user' }];
253+
const contentId = 'content-id';
254+
mockGetInput({
255+
owner,
256+
'project-number': projectNumber,
257+
item,
258+
assignees: assigneeLogins.join(',')
259+
});
260+
vi.mocked(getItem).mockResolvedValue({
261+
id: itemId,
262+
type: 'DRAFT_ISSUE',
263+
projectId,
264+
content: {
265+
id: contentId,
266+
assignees: { nodes: currentAssignees }
267+
}
268+
} as ItemDetails);
269+
vi.mocked(editItem).mockResolvedValue();
270+
271+
await index.editItemAction();
272+
expect(editItemActionSpy).toHaveReturned();
273+
274+
expect(editItem).toHaveBeenCalledWith(
275+
projectId,
276+
contentId,
277+
expect.objectContaining({
278+
assignees: assigneeLogins
279+
})
280+
);
281+
expect(core.setFailed).not.toHaveBeenCalled();
241282
});
242283

243284
it('sets output', async () => {
@@ -247,7 +288,7 @@ describe('editItemAction', () => {
247288
type: 'PULL_REQUEST',
248289
projectId
249290
} as ItemDetails);
250-
vi.mocked(editItem).mockResolvedValue(itemId);
291+
vi.mocked(editItem).mockResolvedValue();
251292

252293
await index.editItemAction();
253294
expect(editItemActionSpy).toHaveReturned();

0 commit comments

Comments
 (0)