From b1874bd88f0279ad573acbd205ccf6d995a1fe68 Mon Sep 17 00:00:00 2001 From: Marc Itzenthaler Date: Mon, 5 Jun 2023 10:50:19 +0200 Subject: [PATCH] fix: flaky tests --- cypress/e2e/appointments.cy.ts | 403 +++++++++--------- cypress/e2e/messages.cy.ts | 12 - cypress/e2e/profile.cy.ts | 2 - cypress/e2e/release-notes.cy.ts | 3 - cypress/e2e/sessions.cy.ts | 17 +- cypress/support/commands.ts | 6 +- cypress/support/commands/mockApi.ts | 36 +- .../provider/SessionsDataProvider.tsx | 4 +- 8 files changed, 250 insertions(+), 233 deletions(-) diff --git a/cypress/e2e/appointments.cy.ts b/cypress/e2e/appointments.cy.ts index 4e9cb4c6f..17ab0caff 100644 --- a/cypress/e2e/appointments.cy.ts +++ b/cypress/e2e/appointments.cy.ts @@ -79,220 +79,231 @@ describe('appointments', () => { }); describe('Video Consultant', () => { - beforeEach(() => { - cy.fastLogin({ - username: USER_VIDEO + describe('Default login', () => { + beforeEach(() => { + cy.fastLogin({ + username: USER_VIDEO + }); }); - }); - - it('Appointments List without appointments', () => { - cy.contains('Video - Termine').should('exist').click(); - cy.wait('@appointments_get'); - cy.get('.appointments').contains('Aktuell gibt es keine Termine'); - }); - it('Add appointment', () => { - // Default Meeting Time - const dMT = new Date(); - dMT.setHours(8); - dMT.setMinutes(0); - - cy.contains('Video - Termine').click(); - cy.wait('@appointments_get'); - - cy.get('.appointments .box').should('not.exist'); - cy.get('.appointments__actions .button__wrapper button').click(); - - cy.get('#overlay .onlineMeetingForm .react-datepicker--date') - .click() - .get( - '#overlay .onlineMeetingForm .react-datepicker--date .react-datepicker .react-datepicker__day--today' - ) - .get( - '#overlay .onlineMeetingForm .react-datepicker--date .react-datepicker .react-datepicker__day--today' - ) - .should('not.have.class', 'react-datepicker__day--disabled') - .click() - .get( - '#overlay .onlineMeetingForm .react-datepicker--time input' - ) - .should( - 'have.value', - `${(dMT.getHours() + 100).toString().substring(1)}:${( - dMT.getMinutes() + 100 - ) - .toString() - .substring(1)}` - ) - .get('#overlay .onlineMeetingForm textarea') - .type('Meine Beschreibung') - .get('#overlay .overlay__buttons button') - .contains('Speichern') - .click(); - cy.wait('@appointments_post'); - - cy.get('.appointments .box').should('have.length', 1); - cy.get('.appointments .box .appointment__description').should( - 'contain.text', - 'Meine Beschreibung' - ); - - cy.appointments(); - }); + it('Appointments List without appointments', () => { + cy.contains('Video - Termine').should('exist').click(); + cy.wait('@appointments_get'); + cy.get('.appointments').contains( + 'Aktuell gibt es keine Termine' + ); + }); - describe('Appointment actions', () => { - beforeEach(() => { - const today = new Date(); - cy.appointments({ - id: uuid(), - description: 'Mein Termin 1', - datetime: today.toISOString() - }); - today.setDate(today.getDate() + 1); - cy.appointments({ - id: uuid(), - description: 'Mein Termin 2', - datetime: today.toISOString() - }); - today.setDate(today.getDate() + 1); - cy.appointments({ - id: uuid(), - description: 'Mein Termin 3', - datetime: today.toISOString() - }); + it('Add appointment', () => { + // Default Meeting Time + const dMT = new Date(); + dMT.setHours(8); + dMT.setMinutes(0); cy.contains('Video - Termine').click(); cy.wait('@appointments_get'); - }); - afterEach(() => { + cy.get('.appointments .box').should('not.exist'); + cy.get( + '.appointments__actions .button__wrapper button' + ).click(); + + cy.get('#overlay .onlineMeetingForm .react-datepicker--date') + .click() + .get( + '#overlay .onlineMeetingForm .react-datepicker--date .react-datepicker .react-datepicker__day--today' + ) + .get( + '#overlay .onlineMeetingForm .react-datepicker--date .react-datepicker .react-datepicker__day--today' + ) + .should('not.have.class', 'react-datepicker__day--disabled') + .click() + .get( + '#overlay .onlineMeetingForm .react-datepicker--time input' + ) + .should( + 'have.value', + `${(dMT.getHours() + 100).toString().substring(1)}:${( + dMT.getMinutes() + 100 + ) + .toString() + .substring(1)}` + ) + .get('#overlay .onlineMeetingForm textarea') + .type('Meine Beschreibung') + .get('#overlay .overlay__buttons button') + .contains('Speichern') + .click(); + cy.wait('@appointments_post'); + + cy.get('.appointments .box').should('have.length', 1); + cy.get('.appointments .box .appointment__description').should( + 'contain.text', + 'Meine Beschreibung' + ); + cy.appointments(); }); - it('Edit appointment', () => { - cy.get('.appointments .box').should('have.length', 3); - cy.get('.appointments .box') - .eq(0) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 1'); - cy.get('.appointments .box') - .eq(1) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 2'); - cy.get('.appointments .box') - .eq(2) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 3'); - - // Edit appointment #1 - handleUiEdit(cy, 0, ' hat jetzt mehr Inhalt', true); - // Edit and cancel appointment #2 - handleUiEdit(cy, 1, ' wurde geändert aber abgebrochen', false); - // Edit appointment #3 - handleUiEdit(cy, 2, ' hat jetzt noch mehr Inhalt', true); - - // Check list - cy.get('.appointments .box') - .eq(0) - .find('.appointment__description') - .should( - 'contain.text', - 'Mein Termin 1 hat jetzt mehr Inhalt' + describe('Appointment actions', () => { + beforeEach(() => { + const today = new Date(); + cy.appointments({ + id: uuid(), + description: 'Mein Termin 1', + datetime: today.toISOString() + }); + today.setDate(today.getDate() + 1); + cy.appointments({ + id: uuid(), + description: 'Mein Termin 2', + datetime: today.toISOString() + }); + today.setDate(today.getDate() + 1); + cy.appointments({ + id: uuid(), + description: 'Mein Termin 3', + datetime: today.toISOString() + }); + + cy.contains('Video - Termine').click(); + cy.wait('@appointments_get'); + }); + + afterEach(() => { + cy.appointments(); + }); + + it('Edit appointment', () => { + cy.get('.appointments .box').should('have.length', 3); + cy.get('.appointments .box') + .eq(0) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 1'); + cy.get('.appointments .box') + .eq(1) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 2'); + cy.get('.appointments .box') + .eq(2) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 3'); + + // Edit appointment #1 + handleUiEdit(cy, 0, ' hat jetzt mehr Inhalt', true); + // Edit and cancel appointment #2 + handleUiEdit( + cy, + 1, + ' wurde geändert aber abgebrochen', + false ); - cy.get('.appointments .box') - .eq(1) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 2'); - cy.get('.appointments .box') - .eq(2) - .find('.appointment__description') - .should( - 'contain.text', - 'Mein Termin 3 hat jetzt noch mehr Inhalt' + // Edit appointment #3 + handleUiEdit(cy, 2, ' hat jetzt noch mehr Inhalt', true); + + // Check list + cy.get('.appointments .box') + .eq(0) + .find('.appointment__description') + .should( + 'contain.text', + 'Mein Termin 1 hat jetzt mehr Inhalt' + ); + cy.get('.appointments .box') + .eq(1) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 2'); + cy.get('.appointments .box') + .eq(2) + .find('.appointment__description') + .should( + 'contain.text', + 'Mein Termin 3 hat jetzt noch mehr Inhalt' + ); + }); + + it('Delete appointment', () => { + cy.get('.appointments .box').should('have.length', 3); + cy.get('.appointments .box') + .eq(0) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 1'); + cy.get('.appointments .box') + .eq(1) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 2'); + cy.get('.appointments .box') + .eq(2) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 3'); + + // Press delete and cancel + handleUiDelete(cy, 1, false); + cy.get('.appointments .box').should('have.length', 3); + + // Press delete and proceed + handleUiDelete(cy, 1, true); + cy.get('.appointments .box').should('have.length', 2); + + cy.get('.appointments .box') + .eq(0) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 1'); + cy.get('.appointments .box') + .eq(1) + .find('.appointment__description') + .should('contain.text', 'Mein Termin 3'); + + handleUiDelete(cy, 0, true); + handleUiDelete(cy, 0, true); + cy.get('.appointments .box').should('have.length', 0); + cy.get('.appointments').contains( + 'Aktuell gibt es keine Termine' ); - }); + }); - it('Delete appointment', () => { - cy.get('.appointments .box').should('have.length', 3); - cy.get('.appointments .box') - .eq(0) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 1'); - cy.get('.appointments .box') - .eq(1) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 2'); - cy.get('.appointments .box') - .eq(2) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 3'); - - // Press delete and cancel - handleUiDelete(cy, 1, false); - cy.get('.appointments .box').should('have.length', 3); - - // Press delete and proceed - handleUiDelete(cy, 1, true); - cy.get('.appointments .box').should('have.length', 2); - - cy.get('.appointments .box') - .eq(0) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 1'); - cy.get('.appointments .box') - .eq(1) - .find('.appointment__description') - .should('contain.text', 'Mein Termin 3'); - - handleUiDelete(cy, 0, true); - handleUiDelete(cy, 0, true); - cy.get('.appointments .box').should('have.length', 0); - cy.get('.appointments').contains( - 'Aktuell gibt es keine Termine' - ); - }); + it('Copy appointment link', () => { + cy.get('.appointments .box').should('have.length', 3); - it('Copy appointment link', () => { - cy.get('.appointments .box').should('have.length', 3); - - cy.get('.appointments .box') - .eq(0) - .find('[data-cy=appointment_url]') - .then(($url) => { - const appointmentLink = $url.text(); - - cy.get('.appointments .box') - .eq(0) - .find('[data-cy=appointment_link] span') - .click(); - - cy.window().then((win) => { - if (win.navigator.clipboard) { - win.navigator.clipboard - .readText() - .then((text) => { - expect(text).to.eq(appointmentLink); - }); - } else { - win.clipboardData - .getData('text/plain') - .then((text) => { - expect(text).to.eq(appointmentLink); - }); - } + cy.get('.appointments .box') + .eq(0) + .find('[data-cy=appointment_url]') + .then(($url) => { + const appointmentLink = $url.text(); + + cy.get('.appointments .box') + .eq(0) + .find('[data-cy=appointment_link] span') + .click(); + + cy.window().then((win) => { + if (win.navigator.clipboard) { + win.navigator.clipboard + .readText() + .then((text) => { + expect(text).to.eq(appointmentLink); + }); + } else { + win.clipboardData + .getData('text/plain') + .then((text) => { + expect(text).to.eq(appointmentLink); + }); + } + }); }); - }); - }); + }); - it('Show appointment qr code', () => { - cy.get('.appointments .box').should('have.length', 3); + it('Show appointment qr code', () => { + cy.get('.appointments .box').should('have.length', 3); - cy.get('.appointments .box') - .eq(0) - .find('[data-cy=appointment_qr_code] button') - .click(); + cy.get('.appointments .box') + .eq(0) + .find('[data-cy=appointment_qr_code] button') + .click(); - cy.get('#overlay .generateQrCode__overlay').should('exist'); + cy.get('#overlay .generateQrCode__overlay').should('exist'); + }); }); }); diff --git a/cypress/e2e/messages.cy.ts b/cypress/e2e/messages.cy.ts index f6d40d0e4..901e0f947 100644 --- a/cypress/e2e/messages.cy.ts +++ b/cypress/e2e/messages.cy.ts @@ -43,7 +43,6 @@ describe('Messages', () => { }); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('[data-cy=session-list-item]').click(); cy.wait('@messages'); @@ -71,7 +70,6 @@ describe('Messages', () => { }); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('[data-cy=session-list-item]').click(); cy.wait('@messages'); @@ -91,7 +89,6 @@ describe('Messages', () => { describe('Initially loading the app', () => { it('should not animate the envelope and no dot visible', () => { cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.navigation__item__count--active').should( 'not.exist' @@ -106,7 +103,6 @@ describe('Messages', () => { describe('New message from Live Service', () => { it.skip('should animate the envelope and initial dot', () => { cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); @@ -129,7 +125,6 @@ describe('Messages', () => { describe('New message from Live Service', () => { it.skip('should animate the envelope and initial dot', () => { cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); @@ -152,7 +147,6 @@ describe('Messages', () => { // ToDo: Test currenlty skipped because its not working like the test tries it.skip('should animate envelope and initial dot and remove dot after message was read', () => { cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.get('.sessionsListItem').first().click({ @@ -197,7 +191,6 @@ describe('Messages', () => { it.skip('should animate the envelope and initial dot', () => { cy.askerSession(); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.get('.navigation__item__count--active').should( @@ -235,7 +228,6 @@ describe('Messages', () => { cy.askerSession({ session: { messagesRead: false } }, 0); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.navigation__item__count--active').should('exist'); cy.get('.navigation__item__count--initial').should('exist'); @@ -251,7 +243,6 @@ describe('Messages', () => { ); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); @@ -278,7 +269,6 @@ describe('Messages', () => { ); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); @@ -302,7 +292,6 @@ describe('Messages', () => { cy.askerSession({ session: { messagesRead: false } }); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); @@ -340,7 +329,6 @@ describe('Messages', () => { cy.askerSession({ session: { messagesRead: false } }); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.cy-socket-connected-stomp'); cy.waitForSubscriptions(['/user/events']); diff --git a/cypress/e2e/profile.cy.ts b/cypress/e2e/profile.cy.ts index 5ac9635e9..4da243baf 100644 --- a/cypress/e2e/profile.cy.ts +++ b/cypress/e2e/profile.cy.ts @@ -188,8 +188,6 @@ describe('profile', () => { }); it('deactivate and activate email notification consultant', () => { - cy.wait('@consultingTypeServiceBaseBasic'); - cy.contains('Profil').should('exist').click(); cy.contains('Einstellungen').should('exist').click(); cy.contains('E-Mail-Benachrichtigungen'); diff --git a/cypress/e2e/release-notes.cy.ts b/cypress/e2e/release-notes.cy.ts index f1eb1819b..ba02e145b 100644 --- a/cypress/e2e/release-notes.cy.ts +++ b/cypress/e2e/release-notes.cy.ts @@ -47,7 +47,6 @@ describe('release-note', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.wait('@releases'); cy.wait('@releases_markup'); @@ -89,7 +88,6 @@ describe('release-note', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.wait('@releases'); cy.wait('@releases_markup'); @@ -110,7 +108,6 @@ describe('release-note', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.releaseNote').should('not.exist'); }); diff --git a/cypress/e2e/sessions.cy.ts b/cypress/e2e/sessions.cy.ts index 7308a5887..8682ded83 100644 --- a/cypress/e2e/sessions.cy.ts +++ b/cypress/e2e/sessions.cy.ts @@ -34,7 +34,6 @@ describe('Sessions', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('a[href="/sessions/consultant/sessionView"]').click(); cy.get('.sessionsListItem').should('exist'); @@ -62,7 +61,6 @@ describe('Sessions', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('a[href="/sessions/consultant/sessionView"]').click(); cy.get('.sessionsListItem').should('have.length', 6); @@ -74,7 +72,6 @@ describe('Sessions', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('a[href="/sessions/consultant/sessionView"]').click(); cy.wait('@consultantSessions'); @@ -103,7 +100,6 @@ describe('Sessions', () => { cy.fastLogin({ username: USER_CONSULTANT }); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('a[href="/sessions/consultant/sessionView"]').click(); cy.get('.sessionsListItem').should('exist'); @@ -138,14 +134,17 @@ describe('Sessions', () => { username: USER_CONSULTANT }); cy.wait('@rcSettingsPublic'); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('a[href="/sessions/consultant/sessionView"]').click(); - cy.wait('@consultantSessions'); + cy.wait('@consultantSessions') + .its('response.statusCode') + .should('eq', 200); cy.get('.sessionsListItem.skeleton').should('not.exist'); cy.get('.sessionsListItem').should('exist'); - cy.willReturn('consultantSessions', 401); + cy.willReturn('consultantSessions', { + statusCode: 401 + }); cy.get('.sessionsList__scrollContainer').scrollTo('bottom'); cy.wait('@consultantSessions'); @@ -158,14 +157,12 @@ describe('Sessions', () => { it('should list my sessions', () => { generateMultipleAskerSessions(3); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('.sessionsListItem').should('have.length', 4); }); it('should show a header with headline', () => { cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('[data-cy=session-list-header]').should('exist'); cy.get('[data-cy=session-list-headline]').contains( 'Meine Beratungen' @@ -179,7 +176,6 @@ describe('Sessions', () => { MAX_ITEMS_TO_SHOW_WELCOME_ILLUSTRATION - 1 ); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('[data-cy=session-list-welcome-illustration]').should( 'exist' @@ -191,7 +187,6 @@ describe('Sessions', () => { MAX_ITEMS_TO_SHOW_WELCOME_ILLUSTRATION ); cy.fastLogin(); - cy.wait('@consultingTypeServiceBaseBasic'); cy.get('[data-cy=session-list-welcome-illustration]').should( 'not.exist' diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 1e200734b..c4e78b389 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -27,7 +27,11 @@ declare global { ): Chainable; addMessage(props?: { [key: string]: any }, index?: number); mockApi(): Chainable; - willReturn(name: string, data: any): Chainable; + willReturn( + name: string, + data: any, + extend?: boolean + ): Chainable; emitDirectMessage(index?: number): Chainable; emitVideoCallRequest(): Chainable; waitForSubscriptions(events: string[]): Chainable; diff --git a/cypress/support/commands/mockApi.ts b/cypress/support/commands/mockApi.ts index dfa537020..1b905baf5 100644 --- a/cypress/support/commands/mockApi.ts +++ b/cypress/support/commands/mockApi.ts @@ -67,9 +67,15 @@ const defaultReturns = { agencyConsultantsLanguages: ['de'] }; -Cypress.Commands.add('willReturn', (name: string, data: any) => { - overrides[name] = data; -}); +Cypress.Commands.add( + 'willReturn', + (name: string, data: any, extend?: boolean) => { + overrides[name] = { + ...(extend ? overrides[name] : {}), + ...data + }; + } +); let username = null; @@ -207,9 +213,11 @@ Cypress.Commands.add('mockApi', () => { req.reply('{}'); }).as('sessionRead'); - cy.intercept('GET', `${endpoints.consultantEnquiriesBase}*`, {}).as( - 'consultantEnquiriesBase' - ); + cy.intercept( + 'GET', + `${endpoints.consultantEnquiriesBase}*`, + JSON.stringify({}) + ).as('consultantEnquiriesBase'); cy.intercept('POST', endpoints.keycloakLogout, {}).as('authLogout'); @@ -245,6 +253,12 @@ Cypress.Commands.add('mockApi', () => { 'apiLogout' ); + cy.intercept( + 'GET', + `${endpoints.rc.users.getStatus}*`, + JSON.stringify({}) + ).as('rcUsersGetStatus'); + cy.intercept( `${endpoints.liveservice}/**/*`, JSON.stringify({ @@ -438,6 +452,11 @@ Cypress.Commands.add( ) => { username = args.username || USER_ASKER; + window.sessionStorage.removeItem('public_key'); + window.sessionStorage.removeItem('private_key'); + cy.clearCookie('lang'); + cy.willReturn('userData', { preferredLanguage: null }, true); + cy.fixture('api.v1.login').then((res) => { if (res.data.authToken) { cy.setCookie('rc_token', res.data.authToken); @@ -466,10 +485,15 @@ Cypress.Commands.add( cy.visit('/app'); cy.wait('@usersData'); + cy.wait('@settings'); + cy.wait('@consultingTypeServiceBaseBasic'); + cy.wait('@fetchMyKeys'); if (username === USER_ASKER) { cy.wait('@askerSessions'); } else { cy.wait('@consultantEnquiriesBase'); } + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); } ); diff --git a/src/globalState/provider/SessionsDataProvider.tsx b/src/globalState/provider/SessionsDataProvider.tsx index e2873caff..b7a8bdf85 100644 --- a/src/globalState/provider/SessionsDataProvider.tsx +++ b/src/globalState/provider/SessionsDataProvider.tsx @@ -68,7 +68,7 @@ function reducer( case UPDATE_SESSIONS: { const { sessions } = action; const newSessions = [...state.sessions]; - sessions.forEach((s) => { + (sessions ?? []).forEach((s) => { const newChatItem = getChatItemForSession(s); const index = newSessions.findIndex((s) => { const chatItem = getChatItemForSession(s); @@ -90,7 +90,7 @@ function reducer( case REMOVE_SESSIONS: { const { ids } = action; const newSessions = [...state.sessions]; - ids.forEach((id) => { + (ids ?? []).forEach((id) => { const index = newSessions.findIndex((s) => { const chatItem = getChatItemForSession(s); return chatItem.id === id || chatItem.groupId === id;