Skip to content

Commit ab6a454

Browse files
committed
fix: more testing
1 parent b1ab373 commit ab6a454

21 files changed

Lines changed: 1189 additions & 115 deletions

‎projects/stream-chat-angular/src/lib/attachment.service.spec.ts‎

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
22
import { first } from 'rxjs/operators';
3-
import { Attachment } from 'stream-chat';
3+
import { Attachment, StreamAPIError } from 'stream-chat';
44
import { AttachmentService } from './attachment.service';
55
import { ChannelService } from './channel.service';
66
import { NotificationService } from './notification.service';
@@ -109,17 +109,37 @@ describe('AttachmentService', () => {
109109
},
110110
]);
111111
await service.filesSelected([file] as any as FileList);
112-
deleteAttachmentSpy.and.rejectWith(new Error('error'));
112+
const message = 'DeleteFile failed with error: "error"';
113+
deleteAttachmentSpy.and.rejectWith(
114+
new StreamAPIError(message, {
115+
code: 4,
116+
status: 400,
117+
response: {
118+
data: { code: 4, message, StatusCode: 400 },
119+
status: 400,
120+
} as unknown as StreamAPIError['response'],
121+
})
122+
);
123+
const notificationService = TestBed.inject(NotificationService);
124+
spyOn(notificationService, 'addTemporaryNotification');
113125
let attachmentUpload!: AttachmentUpload;
114126
service.attachmentUploads$
115127
.pipe(first())
116128
.subscribe((uploads) => (attachmentUpload = uploads[0]));
117-
const uploadedAttachmentsSpy = jasmine.createSpy('uploadedAttachmentsSpy');
118-
service.attachmentUploads$.subscribe(uploadAttachmentsSpy);
119-
uploadedAttachmentsSpy.calls.reset();
120129
await service.deleteAttachment(attachmentUpload);
121130

122-
expect(uploadedAttachmentsSpy).not.toHaveBeenCalled();
131+
expect(deleteAttachmentSpy).toHaveBeenCalledWith(attachmentUpload);
132+
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
133+
'streamChat.Error deleting attachment'
134+
);
135+
let uploads!: AttachmentUpload[];
136+
service.attachmentUploads$
137+
.pipe(first())
138+
.subscribe((value) => (uploads = value));
139+
140+
expect(uploads.length).toBe(1);
141+
expect(uploads[0].file).toBe(file);
142+
expect(uploads[0].state).toBe('success');
123143
});
124144

125145
it('should remove deleted attachments after upload', fakeAsync(() => {

‎projects/stream-chat-angular/src/lib/channel.service.spec.ts‎

Lines changed: 126 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
LocalMessage,
1414
ReactionResponse,
1515
SortParamRequest,
16+
StreamAPIError,
1617
UserResponse,
1718
} from 'stream-chat';
1819
import { ChannelService } from './channel.service';
@@ -33,6 +34,17 @@ import { NotificationService } from './notification.service';
3334
import { toLocalMessage } from './local-message';
3435
import { AttachmentUpload, MessageInput, StreamMessage } from './types';
3536

37+
// The error stream-chat v10 throws for API errors (see api-client.ts)
38+
const apiError = (message: string, code = 4, status = 400) =>
39+
new StreamAPIError(message, {
40+
code,
41+
status,
42+
response: {
43+
data: { code, message, StatusCode: status },
44+
status,
45+
} as unknown as StreamAPIError['response'],
46+
});
47+
3648
type TestChatClient = Omit<
3749
MockStreamChatClient,
3850
'channel' | 'channelManager'
@@ -223,7 +235,7 @@ describe('ChannelService', () => {
223235
const notificationSpy = jasmine.createSpy();
224236
notificationService.notifications$.subscribe(notificationSpy);
225237
notificationSpy.calls.reset();
226-
const error = 'there was an error';
238+
const error = apiError('QueryChannels failed with error: "error"');
227239

228240
await expectAsync(
229241
init(undefined, undefined, undefined, () =>
@@ -249,7 +261,9 @@ describe('ChannelService', () => {
249261

250262
await expectAsync(
251263
init(undefined, undefined, undefined, () =>
252-
mockChatClient.queryChannels.and.rejectWith('there was an error')
264+
mockChatClient.queryChannels.and.rejectWith(
265+
apiError('QueryChannels failed with error: "error"')
266+
)
253267
)
254268
).toBeRejected();
255269

@@ -283,20 +297,21 @@ describe('ChannelService', () => {
283297
expect(calls[2].args[0].state).toBe('success');
284298

285299
spy.calls.reset();
300+
const error = apiError('QueryChannels failed with error: "error"');
286301

287302
await expectAsync(
288303
init(undefined, undefined, undefined, () =>
289-
mockChatClient.queryChannels.and.rejectWith('there was an error')
304+
mockChatClient.queryChannels.and.rejectWith(error)
290305
)
291-
).toBeRejected();
306+
).toBeRejectedWith(error);
292307

293308
calls = spy.calls.all();
294309

295310
expect(calls[0].args[0]?.state).toBe('in-progress');
296311

297312
expect(calls[1].args[0]).toEqual({
298313
state: 'error',
299-
error: 'there was an error',
314+
error,
300315
});
301316

302317
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
@@ -612,7 +627,7 @@ describe('ChannelService', () => {
612627
const channel = generateMockChannels(1)[0];
613628
channel.cid = 'not-initialized';
614629
(channel as unknown as { initialized: boolean }).initialized = false;
615-
const error = new Error('watch failed');
630+
const error = apiError('GetOrCreateChannel failed with error: "error"');
616631
spyOn(channel, 'watch').and.rejectWith(error);
617632
const notificationService = TestBed.inject(NotificationService);
618633
spyOn(notificationService, 'addTemporaryNotification');
@@ -2121,16 +2136,12 @@ describe('ChannelService', () => {
21212136
service.activeChannelMessages$.subscribe((m) => {
21222137
latestMessage = m[m.length - 1];
21232138
});
2124-
spyOn(channel, 'sendMessage').and.rejectWith({
2125-
code: 4,
2126-
status: 400,
2127-
response: {
2128-
data: {
2129-
message:
2130-
'SendMessage failed with error: "a message with ID zitaszuperagetstreamio-9005f6d8-aeb7-42a1-984c-921f94567759 already exists"',
2131-
},
2132-
},
2133-
});
2139+
spyOn(channel, 'sendMessage').and.rejectWith(
2140+
new StreamAPIError(
2141+
'SendMessage failed with error: "a message with ID zitaszuperagetstreamio-9005f6d8-aeb7-42a1-984c-921f94567759 already exists"',
2142+
{ code: 4, status: 400, response: undefined }
2143+
)
2144+
);
21342145
spyOn(channel.messagePaginator, 'ingestItem').and.callThrough();
21352146
await service.resendMessage(latestMessage);
21362147

@@ -2277,25 +2288,17 @@ describe('ChannelService', () => {
22772288
({ file }: { file: File }) => {
22782289
switch (file.name) {
22792290
case 'file_error.jpg':
2280-
return Promise.reject({
2281-
response: {
2282-
data: {
2283-
code: 4,
2284-
message:
2285-
'UploadImage failed with error: "File extension .jpg is not supported"',
2286-
},
2287-
},
2288-
});
2291+
return Promise.reject(
2292+
apiError(
2293+
'UploadImage failed with error: "File extension .jpg is not supported"'
2294+
)
2295+
);
22892296
case 'file_too_big.jpg':
2290-
return Promise.reject({
2291-
response: {
2292-
data: {
2293-
code: 4,
2294-
message:
2295-
'UploadImage failed with error: "File size is above the size limit (1048576 bytes)"',
2296-
},
2297-
},
2298-
});
2297+
return Promise.reject(
2298+
apiError(
2299+
'UploadImage failed with error: "File size is above the size limit (1048576 bytes)"'
2300+
)
2301+
);
22992302
default:
23002303
return Promise.resolve({
23012304
file: 'http://url/to/image',
@@ -2308,15 +2311,13 @@ describe('ChannelService', () => {
23082311
({ file }: { file: File }) => {
23092312
switch (file.name) {
23102313
case 'file_error.pdf':
2311-
return Promise.reject({
2312-
response: {
2313-
data: {
2314-
code: 22,
2315-
message:
2316-
'UploadFile failed with error: "max upload size of 50MB exceeded"',
2317-
},
2318-
},
2319-
});
2314+
return Promise.reject(
2315+
apiError(
2316+
'UploadFile failed with error: "max upload size of 50MB exceeded"',
2317+
22,
2318+
413
2319+
)
2320+
);
23202321
default:
23212322
return Promise.resolve({
23222323
file: 'http://url/to/file',
@@ -2997,7 +2998,7 @@ describe('ChannelService', () => {
29972998
service.activeChannelMessages$.subscribe(messagesSpy);
29982999
messagesSpy.calls.reset();
29993000
const paginator = mockPaginator(getActiveChannel());
3000-
const error = new Error();
3001+
const error = apiError('GetOrCreateChannel failed with error: "error"');
30013002
spyOn(paginator, 'jumpToMessage').and.rejectWith(error);
30023003
const notificationService = TestBed.inject(NotificationService);
30033004
spyOn(notificationService, 'addTemporaryNotification');
@@ -3012,6 +3013,34 @@ describe('ChannelService', () => {
30123013
expect(service.isMessageLoadingInProgress).toBeFalse();
30133014
});
30143015

3016+
it(`should display error notification if message couldn't be loaded - paginator doesn't throw API errors`, async () => {
3017+
await init();
3018+
const jumpToMessageIdSpy = jasmine.createSpy();
3019+
service.jumpToMessage$.subscribe(jumpToMessageIdSpy);
3020+
jumpToMessageIdSpy.calls.reset();
3021+
const paginator = mockPaginator(getActiveChannel());
3022+
// By default (throwErrors: false) the stream-chat paginator records the error and resolves to false
3023+
spyOn(paginator, 'jumpToMessage').and.callFake(() => {
3024+
paginator.state.partialNext({
3025+
lastQueryError: apiError(
3026+
'GetOrCreateChannel failed with error: "error"',
3027+
-1,
3028+
500
3029+
),
3030+
});
3031+
return Promise.resolve(false);
3032+
});
3033+
const notificationService = TestBed.inject(NotificationService);
3034+
spyOn(notificationService, 'addTemporaryNotification');
3035+
await expectAsync(service.jumpToMessage('1232121123')).toBeRejected();
3036+
3037+
expect(jumpToMessageIdSpy).not.toHaveBeenCalled();
3038+
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
3039+
'streamChat.Message not found'
3040+
);
3041+
expect(service.isMessageLoadingInProgress).toBeFalse();
3042+
});
3043+
30153044
it(`should display error notification if message wasn't found`, async () => {
30163045
await init();
30173046
const jumpToMessageIdSpy = jasmine.createSpy();
@@ -3124,7 +3153,7 @@ describe('ChannelService', () => {
31243153
it('should jump to first unread message - error', async () => {
31253154
await init();
31263155
const paginator = mockPaginator(getActiveChannel());
3127-
const error = new Error('error');
3156+
const error = apiError('GetOrCreateChannel failed with error: "error"');
31283157
spyOn(paginator, 'jumpToTheFirstUnreadMessage').and.rejectWith(error);
31293158
const notificationService = TestBed.inject(NotificationService);
31303159
spyOn(notificationService, 'addTemporaryNotification');
@@ -3139,6 +3168,47 @@ describe('ChannelService', () => {
31393168
expect(service.isMessageLoadingInProgress).toBeFalse();
31403169
});
31413170

3171+
it(`should jump to first unread message - error, paginator doesn't throw API errors`, async () => {
3172+
await init();
3173+
const paginator = mockPaginator(getActiveChannel());
3174+
const error = apiError(
3175+
'GetOrCreateChannel failed with error: "error"',
3176+
-1,
3177+
500
3178+
);
3179+
spyOn(paginator, 'jumpToTheFirstUnreadMessage').and.callFake(() => {
3180+
paginator.state.partialNext({ lastQueryError: error });
3181+
return Promise.resolve(false);
3182+
});
3183+
const notificationService = TestBed.inject(NotificationService);
3184+
spyOn(notificationService, 'addTemporaryNotification');
3185+
3186+
await expectAsync(service.jumpToFirstUnreadMessage()).toBeRejectedWith(
3187+
error
3188+
);
3189+
3190+
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
3191+
'streamChat.Message not found'
3192+
);
3193+
expect(service.isMessageLoadingInProgress).toBeFalse();
3194+
});
3195+
3196+
it(`shouldn't display error notification if there is no unread message to jump to`, async () => {
3197+
await init();
3198+
const paginator = mockPaginator(getActiveChannel());
3199+
// An error of an earlier query shouldn't be reported again
3200+
paginator.state.partialNext({
3201+
lastQueryError: apiError('earlier error', -1, 500),
3202+
});
3203+
spyOn(paginator, 'jumpToTheFirstUnreadMessage').and.resolveTo(false);
3204+
const notificationService = TestBed.inject(NotificationService);
3205+
spyOn(notificationService, 'addTemporaryNotification');
3206+
3207+
expect(await service.jumpToFirstUnreadMessage()).toBeFalse();
3208+
expect(notificationService.addTemporaryNotification).not.toHaveBeenCalled();
3209+
expect(service.isMessageLoadingInProgress).toBeFalse();
3210+
});
3211+
31423212
it('should set active parent message', async () => {
31433213
await init();
31443214
const channel = getActiveChannel();
@@ -3252,7 +3322,7 @@ describe('ChannelService', () => {
32523322
parentMessage: channel.messagePaginator.getItem(parentMessage.id)!,
32533323
});
32543324
thread.hasStaleState = true;
3255-
const error = new Error('reload failed');
3325+
const error = apiError('GetThread failed with error: "error"');
32563326
spyOn(thread, 'reload').and.rejectWith(error);
32573327
const notificationService = TestBed.inject(NotificationService);
32583328
spyOn(notificationService, 'addTemporaryNotification');
@@ -3304,7 +3374,7 @@ describe('ChannelService', () => {
33043374
const message = mockMessage();
33053375
const notificationService = TestBed.inject(NotificationService);
33063376
spyOn(notificationService, 'addTemporaryNotification').and.callThrough();
3307-
const error = new Error('error');
3377+
const error = apiError('UpdateMessagePartial failed with error: "error"');
33083378
mockChatClient.pinMessage.and.rejectWith(error);
33093379

33103380
await expectAsync(service.pinMessage(message)).toBeRejectedWith(error);
@@ -3329,7 +3399,7 @@ describe('ChannelService', () => {
33293399
const message = mockMessage();
33303400
const notificationService = TestBed.inject(NotificationService);
33313401
spyOn(notificationService, 'addTemporaryNotification').and.callThrough();
3332-
const error = new Error('error');
3402+
const error = apiError('UpdateMessagePartial failed with error: "error"');
33333403
mockChatClient.unpinMessage.and.rejectWith(error);
33343404

33353405
await expectAsync(service.unpinMessage(message)).toBeRejectedWith(error);
@@ -3573,7 +3643,7 @@ describe('ChannelService', () => {
35733643
it('should load message reactions - error', async () => {
35743644
await init();
35753645
const message = service.activeChannelMessages[0];
3576-
const error = new Error('Reactions loading failed');
3646+
const error = apiError('GetReactions failed with error: "error"');
35773647
mockChatClient.getReactions.and.rejectWith(error);
35783648
const notificationService = TestBed.inject(NotificationService);
35793649
const notificationSpy = jasmine.createSpy();
@@ -3607,12 +3677,10 @@ describe('ChannelService', () => {
36073677
const activeChannel = getActiveChannel();
36083678
const message = service.activeChannelMessages[0];
36093679

3610-
const error = {
3611-
response: {
3612-
data: { code: 4, StatusCode: 400, message: 'You made a mistake' },
3613-
},
3614-
};
3615-
spyOn(activeChannel, 'markUnread').and.rejectWith(error);
3680+
let error = apiError('You made a mistake');
3681+
spyOn(activeChannel, 'markUnread').and.callFake(() =>
3682+
Promise.reject(error)
3683+
);
36163684
const spy = jasmine.createSpy();
36173685
const notificationService = TestBed.inject(NotificationService);
36183686
notificationService.notifications$.subscribe(spy);
@@ -3627,8 +3695,9 @@ describe('ChannelService', () => {
36273695
}),
36283696
]);
36293697

3630-
error.response.data.message =
3631-
'MarkUnread failed with error: "Either the message with ID "zitaszuperagetstreamio-c710c117-5a8e-4769-a43d-f6add79d8520" does not exist, or it is older than last 100 channel messages."';
3698+
error = apiError(
3699+
'MarkUnread failed with error: "Either the message with ID "zitaszuperagetstreamio-c710c117-5a8e-4769-a43d-f6add79d8520" does not exist, or it is older than last 100 channel messages."'
3700+
);
36323701
spy.calls.reset();
36333702
await expectAsync(service.markMessageUnread(message.id)).toBeRejectedWith(
36343703
error

0 commit comments

Comments
 (0)