Skip to content

Commit f4383c0

Browse files
authored
Merge pull request #148 from streamdevs/61-release-created
feat: Implement ReleaseCreated
2 parents 21d7963 + c611c51 commit f4383c0

File tree

8 files changed

+226
-1
lines changed

8 files changed

+226
-1
lines changed

src/reactions/github/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Star } from './star';
1010
import { CheckRun } from './check-run';
1111
import { IssueAssigned } from './issue-assigned';
1212
import { SponsorshipCreated } from './sponsorship-created';
13+
import { ReleaseCreated } from './release-created';
1314

1415
export {
1516
Fork,
@@ -39,5 +40,6 @@ export const reactionBuild = ({
3940
new IssueOpened(twitchChat, streamlabs),
4041
new IssueAssigned(twitchChat, streamlabs),
4142
new SponsorshipCreated(twitchChat, streamlabs),
43+
new ReleaseCreated(twitchChat, streamlabs),
4244
];
4345
};

src/reactions/github/ping.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import { PingPayload } from '../../schemas/github/ping-payload';
33

44
export class Ping extends Reaction<PingPayload> {
55
canHandle({ payload, event }: ReactionCanHandleOptions<PingPayload>): boolean {
6-
const compatibleEvents = ['star', 'fork', 'pull_request', 'issues', 'check_run', 'sponsorship'];
6+
const compatibleEvents = [
7+
'star',
8+
'fork',
9+
'pull_request',
10+
'issues',
11+
'check_run',
12+
'sponsorship',
13+
'release',
14+
];
715

816
return (
917
event === 'ping' &&
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Reaction, ReactionCanHandleOptions, ReactionHandleOptions } from './reaction';
2+
import { ReleaseCreatedPayload } from '../../schemas/github/release-created-payload';
3+
4+
export class ReleaseCreated extends Reaction<ReleaseCreatedPayload> {
5+
canHandle({ payload, event }: ReactionCanHandleOptions<ReleaseCreatedPayload>): boolean {
6+
return event === 'release' && payload.action === 'published';
7+
}
8+
9+
getStreamLabsMessage({ payload }: ReactionHandleOptions<ReleaseCreatedPayload>): string {
10+
const {
11+
repository: { full_name: repositoryFullName },
12+
release: { tag_name: releaseName },
13+
} = payload;
14+
15+
return `*${repositoryFullName}* version *${releaseName}* has just been released 🚀!`;
16+
}
17+
18+
getTwitchChatMessage({ payload }: ReactionHandleOptions<ReleaseCreatedPayload>): string {
19+
const {
20+
repository: { full_name: repositoryFullName },
21+
release: { tag_name: releaseName, html_url: releaseUrl },
22+
} = payload;
23+
24+
return `${repositoryFullName} version ${releaseName} has just been released 🚀! Check it out ${releaseUrl}`;
25+
}
26+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { RepositoryWebhookPayload } from './repository-webhook-payload';
2+
3+
export interface ReleaseCreatedPayload extends RepositoryWebhookPayload {
4+
action: 'unpublished' | 'published' | 'created' | 'edited' | 'deleted' | 'prereleased';
5+
release: {
6+
html_url: string;
7+
tag_name: string;
8+
};
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ReleaseCreatedPayloadBuilder } from './release-created-payload-builder';
2+
import { ReleaseCreatedPayload } from '../../src/schemas/github/release-created-payload';
3+
4+
describe('ReleasePayloadBuilder', () => {
5+
describe('#constructor', () => {
6+
it('instantiates a builder with a default valid payload', () => {
7+
const subject = new ReleaseCreatedPayloadBuilder();
8+
9+
expect(subject.getInstance()).toBeTruthy();
10+
});
11+
});
12+
13+
describe('#with', () => {
14+
it('merges values correctly', () => {
15+
const subject = new ReleaseCreatedPayloadBuilder().with({
16+
repository: { html_url: 'hello' },
17+
} as Partial<ReleaseCreatedPayload>);
18+
19+
expect(subject.getInstance()).toEqual(
20+
expect.objectContaining({
21+
repository: {
22+
html_url: 'hello',
23+
full_name: 'streamdevs/webhook',
24+
},
25+
}),
26+
);
27+
});
28+
});
29+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ReleaseCreatedPayload } from '../../src/schemas/github/release-created-payload';
2+
import { merge } from 'lodash';
3+
4+
export class ReleaseCreatedPayloadBuilder {
5+
private payload: ReleaseCreatedPayload = {
6+
sender: {
7+
login: 'orestes',
8+
},
9+
repository: {
10+
full_name: 'streamdevs/webhook',
11+
html_url: 'http://github.com/streamdevs/webhook',
12+
},
13+
action: 'created',
14+
release: {
15+
tag_name: '1.0.0',
16+
html_url: 'http://github.com/streamdevs/webhook/releases/1.0.0',
17+
},
18+
};
19+
20+
getInstance(): ReleaseCreatedPayload {
21+
return this.payload;
22+
}
23+
24+
with(values: Partial<ReleaseCreatedPayload>): ReleaseCreatedPayloadBuilder {
25+
this.payload = merge(this.payload, values);
26+
27+
return this;
28+
}
29+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { ReleaseCreated } from '../../../src/reactions/github/release-created';
2+
import { TwitchChatMock } from '../../__mocks__/TwitchChat';
3+
import { StreamLabsMock } from '../../__mocks__/StreamLabs';
4+
import { ReleaseCreatedPayload } from '../../../src/schemas/github/release-created-payload';
5+
import { ReleaseCreatedPayloadBuilder } from '../../builders/release-created-payload-builder';
6+
7+
describe('ReleaseCreated', () => {
8+
let twitchChat: TwitchChatMock;
9+
let streamLabs: StreamLabsMock;
10+
11+
beforeEach(() => {
12+
twitchChat = new TwitchChatMock();
13+
streamLabs = new StreamLabsMock();
14+
});
15+
16+
describe('#canHandle', () => {
17+
it('returns false when the event is not release', () => {
18+
const subject = new ReleaseCreated(twitchChat, streamLabs);
19+
const event = 'star';
20+
21+
const payload: ReleaseCreatedPayload = {} as ReleaseCreatedPayload;
22+
23+
expect(subject.canHandle({ payload, event })).toEqual(false);
24+
});
25+
it('returns false when the release is not published', () => {
26+
const subject = new ReleaseCreated(twitchChat, streamLabs);
27+
const event = 'release';
28+
29+
const payload = new ReleaseCreatedPayloadBuilder().with({ action: 'created' }).getInstance();
30+
31+
expect(subject.canHandle({ payload, event })).toEqual(false);
32+
});
33+
it('returns true when the release is published', () => {
34+
const subject = new ReleaseCreated(twitchChat, streamLabs);
35+
const event = 'release';
36+
37+
const payload = new ReleaseCreatedPayloadBuilder()
38+
.with({ action: 'published' })
39+
.getInstance();
40+
41+
expect(subject.canHandle({ payload, event })).toEqual(true);
42+
});
43+
});
44+
45+
describe('#getTwitchChatMessage', () => {
46+
it('returns the expected message', () => {
47+
const subject = new ReleaseCreated(twitchChat, streamLabs);
48+
const payload = new ReleaseCreatedPayloadBuilder()
49+
.with({ action: 'published' })
50+
.getInstance();
51+
52+
expect(subject.getTwitchChatMessage({ payload })).toEqual(
53+
`streamdevs/webhook version 1.0.0 has just been released 🚀! Check it out http://github.com/streamdevs/webhook/releases/1.0.0`,
54+
);
55+
});
56+
});
57+
58+
describe('#getStreamLabsMessage', () => {
59+
it('returns the expected message', () => {
60+
const subject = new ReleaseCreated(twitchChat, streamLabs);
61+
const payload = new ReleaseCreatedPayloadBuilder()
62+
.with({ action: 'published' })
63+
.getInstance();
64+
65+
expect(subject.getStreamLabsMessage({ payload })).toEqual(
66+
`*streamdevs/webhook* version *1.0.0* has just been released 🚀!`,
67+
);
68+
});
69+
});
70+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { initServer } from '../../../src/server';
2+
import { getConfig } from '../../../src/config';
3+
import { StreamLabs } from '../../../src/services/StreamLabs';
4+
import { TwitchChat } from '../../../src/services/TwitchChat';
5+
import { ReleaseCreatedPayloadBuilder } from '../../builders/release-created-payload-builder';
6+
import { ReleaseCreatedPayload } from '../../../src/schemas/github/release-created-payload';
7+
import { WebhookResponse } from '../../../src/schemas/webhook-response';
8+
9+
describe('POST /github', () => {
10+
let streamLabsSpy: jest.SpyInstance<Promise<void>>;
11+
let twitchChatSpy: jest.SpyInstance<Promise<void>>;
12+
13+
beforeEach(() => {
14+
streamLabsSpy = jest.spyOn(StreamLabs.prototype, 'alert');
15+
streamLabsSpy.mockImplementationOnce(jest.fn());
16+
17+
twitchChatSpy = jest.spyOn(TwitchChat.prototype, 'send');
18+
twitchChatSpy.mockImplementationOnce(jest.fn());
19+
});
20+
21+
it('handles release events', async () => {
22+
const payload = new ReleaseCreatedPayloadBuilder()
23+
.with({
24+
action: 'published',
25+
release: { tag_name: '1.0.0' },
26+
} as Partial<ReleaseCreatedPayload>)
27+
.getInstance();
28+
29+
const subject = await initServer(getConfig());
30+
31+
const response = await subject.inject({
32+
method: 'POST',
33+
url: '/github',
34+
payload,
35+
headers: {
36+
'x-github-event': 'release',
37+
},
38+
});
39+
40+
expect((response.result as WebhookResponse).messages).toEqual(
41+
expect.arrayContaining([
42+
expect.objectContaining({
43+
twitchChat: {
44+
message:
45+
'streamdevs/webhook version 1.0.0 has just been released 🚀! Check it out http://github.com/streamdevs/webhook/releases/1.0.0',
46+
notified: true,
47+
},
48+
}),
49+
]),
50+
);
51+
});
52+
});

0 commit comments

Comments
 (0)