Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit c9270d8

Browse files
committed
Add test when we fail to refresh and latest event is a threaded message
See #8354 (comment)
1 parent 0e923b7 commit c9270d8

File tree

5 files changed

+147
-25
lines changed

5 files changed

+147
-25
lines changed

cypress/e2e/x-msc2716-historical-import/historical-import.spec.ts

Lines changed: 130 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ function sendMarkerEventAndEnsureHistoryDetectedStatusBar(asMatrixClient) {
235235
});
236236

237237
// Ensure the "History import detected" notice is shown
238-
cy.get(`[data-cy="historical-import-detected-status-bar"]`).should("exist");
238+
cy.get(`[data-test-id="historical-import-detected-status-bar"]`).should("exist");
239239
}
240240

241241
/**
@@ -361,6 +361,9 @@ describe("MSC2716: Historical Import", () => {
361361
const AS_TOKEN = 'as_token123';
362362

363363
beforeEach(() => {
364+
// Default threads to ON for this spec
365+
cy.enableLabsFeature("feature_thread");
366+
364367
cy.window().then(win => {
365368
// Collapse left panel for these tests (get more space in the area we care about)
366369
win.localStorage.setItem("mx_lhs_size", "0");
@@ -409,7 +412,7 @@ describe("MSC2716: Historical Import", () => {
409412
});
410413

411414
// Press "Refresh timeline"
412-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
415+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
413416

414417
// Ensure historical messages are now shown
415418
cy.all([
@@ -434,7 +437,7 @@ describe("MSC2716: Historical Import", () => {
434437
});
435438

436439
// Press "Refresh timeline"
437-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
440+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
438441

439442
// Ensure historical messages are now shown
440443
cy.all([
@@ -456,7 +459,7 @@ describe("MSC2716: Historical Import", () => {
456459
sendMarkerEventAndEnsureHistoryDetectedStatusBar(asMatrixClient);
457460

458461
// Press "Refresh timeline"
459-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
462+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
460463

461464
// Ensure all of the messages still show afterwards
462465
cy.all([
@@ -508,11 +511,11 @@ describe("MSC2716: Historical Import", () => {
508511
});
509512

510513
// Press "Refresh timeline"
511-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
514+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
512515

513516
// Wait for the timeline to go blank (meaning it was reset)
514517
// and in the middle of the refrsehing timeline function.
515-
cy.get('[data-cy="message-list"] [data-event-id]')
518+
cy.get('[data-test-id="message-list"] [data-event-id]')
516519
.should('not.exist');
517520

518521
// Then make a `/sync` happen by sending a message and seeing that it
@@ -608,13 +611,13 @@ describe("MSC2716: Historical Import", () => {
608611
});
609612

610613
// Press "Refresh timeline"
611-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
614+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
612615

613616
// Make sure the request was intercepted and thew an error
614617
cy.wait('@contextRequestThatWillTryToMakeNewTimeline').its('response.statusCode').should('eq', 500);
615618

616619
// Make sure we tell the user that an error happened
617-
cy.get(`[data-cy="historical-import-detected-error-content"]`).should("exist");
620+
cy.get(`[data-test-id="historical-import-detected-error-content"]`).should("exist");
618621

619622
// Allow the requests to succeed now
620623
cy.all([
@@ -633,7 +636,7 @@ describe("MSC2716: Historical Import", () => {
633636
});
634637

635638
// Press "Refresh timeline" again, this time the network request should succeed
636-
cy.get(`[data-cy="refresh-timeline-button"]`).click();
639+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
637640

638641
// Make sure the request was intercepted and succeeded
639642
cy.wait('@contextRequestThatWillMakeNewTimeline').its('response.statusCode').should('eq', 200);
@@ -674,4 +677,122 @@ describe("MSC2716: Historical Import", () => {
674677
]);
675678
});
676679
});
680+
681+
it.only("Perfectly resolves timelines when refresh fails and then another refresh causes `getLatestTimeline()` " +
682+
"finds a threaded event", () => {
683+
setupRoomWithHistoricalMessagesAndMarker({
684+
synapse,
685+
asMatrixClient,
686+
virtualUserIDs,
687+
});
688+
689+
// Send a threaded message so it's the latest message in the room
690+
cy.get<string>("@roomId").then(async (roomId) => {
691+
const { event_id: eventIdToThreadFrom } = await asMatrixClient.sendMessage(roomId, null, {
692+
body: `event to thread from (root)`,
693+
msgtype: "m.text",
694+
});
695+
const { event_id: eventIdThreadedMessage } = await asMatrixClient.sendMessage(roomId, null, {
696+
"body": `threaded message1`,
697+
"msgtype": "m.text",
698+
"m.relates_to": {
699+
"rel_type": "m.thread",
700+
"event_id": eventIdToThreadFrom,
701+
"is_falling_back": true,
702+
"m.in_reply_to": {
703+
"event_id": eventIdToThreadFrom,
704+
},
705+
},
706+
});
707+
708+
// Wait for the message to show up for the logged in user
709+
waitForEventIdsInClient([eventIdToThreadFrom]);
710+
cy.wrap(eventIdToThreadFrom).as('eventIdToThreadFrom');
711+
// We don't wait for this event in the client because it will be
712+
// hidden away in a thread.
713+
cy.wrap(eventIdThreadedMessage).as('eventIdThreadedMessage');
714+
715+
// Wait for the thread summary to appear which indicates that
716+
// `eventIdThreadedMessage` made it to the client
717+
cy.get(`[data-event-id="${eventIdToThreadFrom}"] [data-test-id="thread-summary"]`);
718+
});
719+
720+
// Make the `/context` fail when we try to refresh the timeline. We want
721+
// to make sure that we are resilient to this type of failure and can
722+
// retry and recover.
723+
cy.all([
724+
cy.get<string>("@roomId"),
725+
cy.get<string>("@eventIdToThreadFrom"),
726+
]).then(async ([roomId, eventIdToThreadFrom]) => {
727+
// We're using `eventIdToThreadFrom` here because it's the latest
728+
// event in the rooms main timeline which the refresh timeline logic
729+
// will use if available.
730+
const prefix = '/_matrix/client/r0';
731+
const path = `/rooms/${encodeURIComponent(roomId)}/context/${encodeURIComponent(eventIdToThreadFrom)}`;
732+
const contextUrl = `${synapse.baseUrl}${prefix}${path}*`;
733+
cy.intercept(contextUrl, {
734+
statusCode: 500,
735+
body: {
736+
errcode: 'CYPRESS_FAKE_ERROR',
737+
error: 'We purposely intercepted this /context request to make it fail ' +
738+
'in order to test whether the refresh timeline code is resilient',
739+
},
740+
}).as('contextRequestThatWillTryToMakeNewTimeline');
741+
});
742+
743+
// Press "Refresh timeline"
744+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
745+
746+
// Make sure the request was intercepted and thew an error
747+
cy.wait('@contextRequestThatWillTryToMakeNewTimeline').its('response.statusCode').should('eq', 500);
748+
749+
// Wait for the timeline to go blank (meaning it was reset)
750+
// and refreshing the timeline failed.
751+
cy.get('[data-test-id="message-list"] [data-event-id]')
752+
.should('not.exist');
753+
754+
// Press "Refresh timeline" again, this time the network request should succeed.
755+
//
756+
// Since the timeline is now blank, we have no most recent event to
757+
// draw from locally. So `MatrixClient::getLatestTimeline` will
758+
// fetch the latest from `/messages` which will return
759+
// `eventIdThreadedMessage` as the latest event in the room.
760+
cy.get(`[data-test-id="refresh-timeline-button"]`).click();
761+
762+
// Make sure sync pagination still works by seeing a new message show up
763+
cy.get<string>("@roomId").then(async (roomId) => {
764+
const { event_id: eventIdAfterRefresh } = await asMatrixClient.sendMessage(roomId, null, {
765+
body: `live_event after refresh`,
766+
msgtype: "m.text",
767+
});
768+
769+
// Wait for the message to show up for the logged in user
770+
waitForEventIdsInClient([eventIdAfterRefresh]);
771+
772+
cy.wrap(eventIdAfterRefresh).as('eventIdAfterRefresh');
773+
});
774+
775+
// Ensure historical messages are now shown
776+
cy.all([
777+
cy.get<string[]>("@liveMessageEventIds"),
778+
cy.get<string[]>("@historicalEventIds"),
779+
cy.get<string>("@eventIdToThreadFrom"),
780+
cy.get<string>("@eventIdAfterRefresh"),
781+
]).then(async ([
782+
liveMessageEventIds,
783+
historicalEventIds,
784+
eventIdToThreadFrom,
785+
eventIdAfterRefresh,
786+
]) => {
787+
// FIXME: Assert that they appear in the correct order
788+
waitForEventIdsInClient([
789+
liveMessageEventIds[0],
790+
liveMessageEventIds[1],
791+
...historicalEventIds,
792+
liveMessageEventIds[2],
793+
eventIdToThreadFrom,
794+
eventIdAfterRefresh,
795+
]);
796+
});
797+
});
677798
});

src/components/structures/RoomStatusBar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
341341
<AccessibleButton
342342
onClick={this.onRefreshTimelineClick}
343343
className="mx_RoomStatusBar_refreshTimelineBtn"
344-
data-cy="refresh-timeline-button"
344+
data-test-id="refresh-timeline-button"
345345
>
346346
{ _t("Refresh timeline") }
347347
</AccessibleButton>
@@ -382,7 +382,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
382382

383383
errorContent = <>
384384
<hr />
385-
<div className="mx_RoomStatusBar_unsentDescription" data-cy="historical-import-detected-error-content">
385+
<div className="mx_RoomStatusBar_unsentDescription" data-test-id="historical-import-detected-error-content">
386386
{ errorTextContent }
387387
{ " " }
388388
{ submitDebugLogsTextContent }
@@ -391,7 +391,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
391391
}
392392

393393
return (
394-
<div className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages" data-cy="historical-import-detected-status-bar">
394+
<div className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages" data-test-id="historical-import-detected-status-bar">
395395
<div role="alert">
396396
<div className="mx_RoomStatusBar_unsentBadge">
397397
<img

src/components/structures/ScrollPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ export default class ScrollPanel extends React.Component<IProps> {
937937
>
938938
{ this.props.fixedChildren }
939939
<div className="mx_RoomView_messageListWrapper">
940-
<ol ref={this.itemlist} className="mx_RoomView_MessageList" aria-live="polite" data-cy="message-list">
940+
<ol ref={this.itemlist} className="mx_RoomView_MessageList" aria-live="polite" data-test-id="message-list">
941941
{ this.props.children }
942942
</ol>
943943
</div>

src/components/views/rooms/ThreadSummary.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const ThreadSummary = ({ mxEvent, thread }: IProps) => {
4949
return (
5050
<AccessibleButton
5151
className="mx_ThreadSummary"
52+
data-test-id="thread-summary"
5253
onClick={(ev: ButtonEvent) => {
5354
showThread({
5455
rootEvent: mxEvent,

test/components/structures/__snapshots__/RoomStatusBar-test.tsx.snap

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,7 +2189,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should refres
21892189
>
21902190
<div
21912191
className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages"
2192-
data-cy="historical-import-detected-status-bar"
2192+
data-test-id="historical-import-detected-status-bar"
21932193
>
21942194
<div
21952195
role="alert"
@@ -2776,7 +2776,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
27762776
>
27772777
<div
27782778
className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages"
2779-
data-cy="historical-import-detected-status-bar"
2779+
data-test-id="historical-import-detected-status-bar"
27802780
>
27812781
<div
27822782
role="alert"
@@ -2805,7 +2805,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
28052805
<hr />
28062806
<div
28072807
className="mx_RoomStatusBar_unsentDescription"
2808-
data-cy="historical-import-detected-error-content"
2808+
data-test-id="historical-import-detected-error-content"
28092809
>
28102810
An error occurred while trying to refresh the timeline.
28112811
@@ -2838,15 +2838,15 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
28382838
>
28392839
<AccessibleButton
28402840
className="mx_RoomStatusBar_refreshTimelineBtn"
2841-
data-cy="refresh-timeline-button"
2841+
data-test-id="refresh-timeline-button"
28422842
element="div"
28432843
onClick={[Function]}
28442844
role="button"
28452845
tabIndex={0}
28462846
>
28472847
<div
28482848
className="mx_AccessibleButton mx_RoomStatusBar_refreshTimelineBtn"
2849-
data-cy="refresh-timeline-button"
2849+
data-test-id="refresh-timeline-button"
28502850
onClick={[Function]}
28512851
onKeyDown={[Function]}
28522852
onKeyUp={[Function]}
@@ -3391,7 +3391,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
33913391
>
33923392
<div
33933393
className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages"
3394-
data-cy="historical-import-detected-status-bar"
3394+
data-test-id="historical-import-detected-status-bar"
33953395
>
33963396
<div
33973397
role="alert"
@@ -3420,7 +3420,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
34203420
<hr />
34213421
<div
34223422
className="mx_RoomStatusBar_unsentDescription"
3423-
data-cy="historical-import-detected-error-content"
3423+
data-test-id="historical-import-detected-error-content"
34243424
>
34253425
A network error occurred while trying to refresh the timeline. Your homeserver might be down or was just a temporary problem with your internet connection.
34263426
@@ -3431,15 +3431,15 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show e
34313431
>
34323432
<AccessibleButton
34333433
className="mx_RoomStatusBar_refreshTimelineBtn"
3434-
data-cy="refresh-timeline-button"
3434+
data-test-id="refresh-timeline-button"
34353435
element="div"
34363436
onClick={[Function]}
34373437
role="button"
34383438
tabIndex={0}
34393439
>
34403440
<div
34413441
className="mx_AccessibleButton mx_RoomStatusBar_refreshTimelineBtn"
3442-
data-cy="refresh-timeline-button"
3442+
data-test-id="refresh-timeline-button"
34433443
onClick={[Function]}
34443444
onKeyDown={[Function]}
34453445
onKeyUp={[Function]}
@@ -3984,7 +3984,7 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show t
39843984
>
39853985
<div
39863986
className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages"
3987-
data-cy="historical-import-detected-status-bar"
3987+
data-test-id="historical-import-detected-status-bar"
39883988
>
39893989
<div
39903990
role="alert"
@@ -4016,15 +4016,15 @@ exports[`RoomStatusBar timeline needs refresh bar (history import) should show t
40164016
>
40174017
<AccessibleButton
40184018
className="mx_RoomStatusBar_refreshTimelineBtn"
4019-
data-cy="refresh-timeline-button"
4019+
data-test-id="refresh-timeline-button"
40204020
element="div"
40214021
onClick={[Function]}
40224022
role="button"
40234023
tabIndex={0}
40244024
>
40254025
<div
40264026
className="mx_AccessibleButton mx_RoomStatusBar_refreshTimelineBtn"
4027-
data-cy="refresh-timeline-button"
4027+
data-test-id="refresh-timeline-button"
40284028
onClick={[Function]}
40294029
onKeyDown={[Function]}
40304030
onKeyUp={[Function]}

0 commit comments

Comments
 (0)