Skip to content

Commit 438b25e

Browse files
authored
feat: workflow actions (#38)
1 parent ac80511 commit 438b25e

33 files changed

+48838
-1
lines changed

.github/workflows/integration-tests.yml

+40
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,46 @@ jobs:
360360
assert.strictEqual('${{ steps.get-project.outputs.description }}', 'New Description');
361361
assert.strictEqual('${{ steps.get-project.outputs.readme }}', 'This is the readme');
362362
363+
- name: Find Workflow
364+
uses: ./find-workflow/
365+
id: find-workflow
366+
with:
367+
owner: ${{ matrix.owner }}
368+
project-number: ${{ steps.copy-project.outputs.number }}
369+
name: 'Item closed'
370+
token: ${{ steps.get-auth-token.outputs.token }}
371+
372+
- name: Check Workflow Values
373+
uses: ./github-script/
374+
with:
375+
script: |
376+
const assert = require('node:assert');
377+
378+
assert.strictEqual('${{ steps.find-workflow.outputs.name }}', 'Item closed');
379+
assert.strictEqual(${{ steps.find-workflow.outputs.enabled }}, true);
380+
assert.strictEqual('${{ steps.find-workflow.outputs.project-id }}', '${{ steps.get-project.outputs.id }}');
381+
382+
- name: Get Workflow
383+
uses: ./get-workflow/
384+
id: get-workflow
385+
with:
386+
owner: ${{ matrix.owner }}
387+
project-number: ${{ steps.copy-project.outputs.number }}
388+
number: ${{ steps.find-workflow.outputs.number }}
389+
token: ${{ steps.get-auth-token.outputs.token }}
390+
391+
- name: Check Workflow Values
392+
uses: ./github-script/
393+
with:
394+
script: |
395+
const assert = require('node:assert');
396+
397+
assert.strictEqual('${{ steps.get-workflow.outputs.id }}', '${{ steps.find-workflow.outputs.id }}');
398+
assert.strictEqual('${{ steps.get-workflow.outputs.name }}', 'Item closed');
399+
assert.strictEqual('${{ steps.get-workflow.outputs.number }}', '${{ steps.find-workflow.outputs.number }}');
400+
assert.strictEqual(${{ steps.get-workflow.outputs.enabled }}, true);
401+
assert.strictEqual('${{ steps.get-workflow.outputs.project-id }}', '${{ steps.get-project.outputs.id }}');
402+
363403
- name: Close Project
364404
uses: ./close-project/
365405
id: close-project

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ private repositories the PAT must also have the `repo` scope.
3232
| [`project-actions/edit-item`](edit-item) | Edit an item on a project |
3333
| [`project-actions/edit-project`](edit-project) | Edit a project |
3434
| [`project-actions/find-project`](find-project) | Find a project |
35+
| [`project-actions/find-workflow`](find-workflow) | Find a project workflow |
3536
| [`project-actions/get-item`](get-item) | Get an item on a project |
3637
| [`project-actions/get-project`](get-project) | Get a project |
38+
| [`project-actions/get-workflow`](get-workflow) | Get a project workflow |
3739
| [`project-actions/github-script`](github-script) | Modify projects programmatically |
3840
| [`project-actions/link-project`](link-project) | Link a project to a repository or team |
3941

__tests__/find-workflow.test.ts

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
3+
import * as core from '@actions/core';
4+
5+
import * as index from '../src/find-workflow';
6+
import { findWorkflow } from '../src/lib';
7+
import { mockGetBooleanInput, mockGetInput } from './utils';
8+
9+
vi.mock('@actions/core');
10+
vi.mock('../src/lib');
11+
12+
// Spy the action's entrypoint
13+
const findWorkflowActionSpy = vi.spyOn(index, 'findWorkflowAction');
14+
15+
const owner = 'dsanders11';
16+
const projectNumber = '94';
17+
const name = 'workflow-name';
18+
const projectId = 'project-id';
19+
const workflowId = 'workflow-id';
20+
const workflowNumber = 42;
21+
22+
describe('findWorkflowAction', () => {
23+
beforeEach(() => {
24+
vi.clearAllMocks();
25+
});
26+
27+
it('requires the project-number input', async () => {
28+
mockGetInput({ owner });
29+
30+
await index.findWorkflowAction();
31+
expect(findWorkflowActionSpy).toHaveReturned();
32+
33+
expect(core.setFailed).toHaveBeenCalledTimes(1);
34+
expect(core.setFailed).toHaveBeenLastCalledWith(
35+
'Input required and not supplied: project-number'
36+
);
37+
});
38+
39+
it('requires the name input', async () => {
40+
mockGetInput({ owner, 'project-number': projectNumber });
41+
42+
await index.findWorkflowAction();
43+
expect(findWorkflowActionSpy).toHaveReturned();
44+
45+
expect(core.setFailed).toHaveBeenCalledTimes(1);
46+
expect(core.setFailed).toHaveBeenLastCalledWith(
47+
'Input required and not supplied: name'
48+
);
49+
});
50+
51+
it('handles workflow not found', async () => {
52+
mockGetInput({ owner, 'project-number': projectNumber, name });
53+
mockGetBooleanInput({ 'fail-if-workflow-not-found': true });
54+
vi.mocked(findWorkflow).mockResolvedValue(null);
55+
56+
await index.findWorkflowAction();
57+
expect(findWorkflowActionSpy).toHaveReturned();
58+
59+
expect(core.setFailed).toHaveBeenCalledTimes(1);
60+
expect(core.setFailed).toHaveBeenLastCalledWith(
61+
`Workflow not found: ${name}`
62+
);
63+
});
64+
65+
it('can ignore item not found', async () => {
66+
mockGetInput({ owner, 'project-number': projectNumber, name });
67+
mockGetBooleanInput({ 'fail-if-workflow-not-found': false });
68+
vi.mocked(findWorkflow).mockResolvedValue(null);
69+
70+
await index.findWorkflowAction();
71+
expect(findWorkflowActionSpy).toHaveReturned();
72+
73+
expect(core.setFailed).not.toHaveBeenCalled();
74+
expect(core.setOutput).not.toHaveBeenCalled();
75+
});
76+
77+
it('handles generic errors', async () => {
78+
mockGetInput({ owner, 'project-number': projectNumber, name });
79+
vi.mocked(findWorkflow).mockImplementation(() => {
80+
throw new Error('Server error');
81+
});
82+
83+
await index.findWorkflowAction();
84+
expect(findWorkflowActionSpy).toHaveReturned();
85+
86+
expect(core.setFailed).toHaveBeenCalledTimes(1);
87+
expect(core.setFailed).toHaveBeenLastCalledWith('Server error');
88+
});
89+
90+
it('stringifies non-errors', async () => {
91+
mockGetInput({ owner, 'project-number': projectNumber, name });
92+
vi.mocked(findWorkflow).mockImplementation(() => {
93+
throw 42; // eslint-disable-line no-throw-literal
94+
});
95+
96+
await index.findWorkflowAction();
97+
expect(findWorkflowActionSpy).toHaveReturned();
98+
99+
expect(core.setFailed).toHaveBeenCalledTimes(1);
100+
expect(core.setFailed).toHaveBeenLastCalledWith('42');
101+
});
102+
103+
it('sets output', async () => {
104+
mockGetInput({ owner, 'project-number': projectNumber, name });
105+
vi.mocked(findWorkflow).mockResolvedValue({
106+
id: workflowId,
107+
name,
108+
number: workflowNumber,
109+
enabled: true,
110+
projectId
111+
});
112+
113+
await index.findWorkflowAction();
114+
expect(findWorkflowActionSpy).toHaveReturned();
115+
116+
expect(core.setOutput).toHaveBeenCalledTimes(5);
117+
expect(core.setOutput).toHaveBeenCalledWith('id', workflowId);
118+
expect(core.setOutput).toHaveBeenCalledWith('name', name);
119+
expect(core.setOutput).toHaveBeenCalledWith('number', workflowNumber);
120+
expect(core.setOutput).toHaveBeenCalledWith('enabled', true);
121+
expect(core.setOutput).toHaveBeenCalledWith('project-id', projectId);
122+
});
123+
});

__tests__/get-workflow.test.ts

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
3+
import * as core from '@actions/core';
4+
5+
import * as index from '../src/get-workflow';
6+
import { getWorkflow } from '../src/lib';
7+
import { mockGetBooleanInput, mockGetInput } from './utils';
8+
9+
vi.mock('@actions/core');
10+
vi.mock('../src/lib');
11+
12+
// Spy the action's entrypoint
13+
const getWorkflowActionSpy = vi.spyOn(index, 'getWorkflowAction');
14+
15+
const owner = 'dsanders11';
16+
const projectNumber = '94';
17+
const name = 'workflow-name';
18+
const projectId = 'project-id';
19+
const workflowId = 'workflow-id';
20+
const number = '42';
21+
22+
describe('getWorkflowAction', () => {
23+
beforeEach(() => {
24+
vi.clearAllMocks();
25+
});
26+
27+
it('requires the project-number input', async () => {
28+
mockGetInput({ owner });
29+
30+
await index.getWorkflowAction();
31+
expect(getWorkflowActionSpy).toHaveReturned();
32+
33+
expect(core.setFailed).toHaveBeenCalledTimes(1);
34+
expect(core.setFailed).toHaveBeenLastCalledWith(
35+
'Input required and not supplied: project-number'
36+
);
37+
});
38+
39+
it('requires the number input', async () => {
40+
mockGetInput({ owner, 'project-number': projectNumber });
41+
42+
await index.getWorkflowAction();
43+
expect(getWorkflowActionSpy).toHaveReturned();
44+
45+
expect(core.setFailed).toHaveBeenCalledTimes(1);
46+
expect(core.setFailed).toHaveBeenLastCalledWith(
47+
'Input required and not supplied: number'
48+
);
49+
});
50+
51+
it('handles workflow not found', async () => {
52+
mockGetInput({ owner, 'project-number': projectNumber, number });
53+
mockGetBooleanInput({ 'fail-if-workflow-not-found': true });
54+
vi.mocked(getWorkflow).mockResolvedValue(null);
55+
56+
await index.getWorkflowAction();
57+
expect(getWorkflowActionSpy).toHaveReturned();
58+
59+
expect(core.setFailed).toHaveBeenCalledTimes(1);
60+
expect(core.setFailed).toHaveBeenLastCalledWith(
61+
`Workflow not found: ${number}`
62+
);
63+
});
64+
65+
it('can ignore item not found', async () => {
66+
mockGetInput({ owner, 'project-number': projectNumber, number });
67+
mockGetBooleanInput({ 'fail-if-workflow-not-found': false });
68+
vi.mocked(getWorkflow).mockResolvedValue(null);
69+
70+
await index.getWorkflowAction();
71+
expect(getWorkflowActionSpy).toHaveReturned();
72+
73+
expect(core.setFailed).not.toHaveBeenCalled();
74+
expect(core.setOutput).not.toHaveBeenCalled();
75+
});
76+
77+
it('handles generic errors', async () => {
78+
mockGetInput({ owner, 'project-number': projectNumber, number });
79+
vi.mocked(getWorkflow).mockImplementation(() => {
80+
throw new Error('Server error');
81+
});
82+
83+
await index.getWorkflowAction();
84+
expect(getWorkflowActionSpy).toHaveReturned();
85+
86+
expect(core.setFailed).toHaveBeenCalledTimes(1);
87+
expect(core.setFailed).toHaveBeenLastCalledWith('Server error');
88+
});
89+
90+
it('stringifies non-errors', async () => {
91+
mockGetInput({ owner, 'project-number': projectNumber, number });
92+
vi.mocked(getWorkflow).mockImplementation(() => {
93+
throw 42; // eslint-disable-line no-throw-literal
94+
});
95+
96+
await index.getWorkflowAction();
97+
expect(getWorkflowActionSpy).toHaveReturned();
98+
99+
expect(core.setFailed).toHaveBeenCalledTimes(1);
100+
expect(core.setFailed).toHaveBeenLastCalledWith('42');
101+
});
102+
103+
it('sets output', async () => {
104+
mockGetInput({ owner, 'project-number': projectNumber, number });
105+
vi.mocked(getWorkflow).mockResolvedValue({
106+
id: workflowId,
107+
name,
108+
number: parseInt(number),
109+
enabled: true,
110+
projectId
111+
});
112+
113+
await index.getWorkflowAction();
114+
expect(getWorkflowActionSpy).toHaveReturned();
115+
116+
expect(core.setOutput).toHaveBeenCalledTimes(5);
117+
expect(core.setOutput).toHaveBeenCalledWith('id', workflowId);
118+
expect(core.setOutput).toHaveBeenCalledWith('name', name);
119+
expect(core.setOutput).toHaveBeenCalledWith('number', parseInt(number));
120+
expect(core.setOutput).toHaveBeenCalledWith('enabled', true);
121+
expect(core.setOutput).toHaveBeenCalledWith('project-id', projectId);
122+
});
123+
});

0 commit comments

Comments
 (0)