diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04639bcb..c0575f61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: yarn install @@ -96,7 +96,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: yarn install @@ -184,7 +184,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: yarn install @@ -221,7 +221,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: yarn install diff --git a/README.md b/README.md index 9f5beb1d..f0bf5000 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Check the following steps to develop for the widget: ### Requirements -You need to install Node.js (`>= 16.0.0`, prefer using an LTS version) and run +You need to install Node.js (`>= 20.0.0`, prefer using an LTS version) and run `yarn` to work on this package. The minimal Element version to use this widget is `1.11.29`. diff --git a/e2e/package.json b/e2e/package.json index da6369f4..7b09d6b6 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -5,14 +5,12 @@ "license": "Apache-2.0", "version": "0.0.0", "private": true, - "dependencies": { - "cross-fetch": "^4.0.0" - }, + "dependencies": {}, "devDependencies": { "@axe-core/playwright": "^4.7.3", "@playwright/test": "^1.37.1", "@types/lodash": "^4.14.197", - "@types/node": "^16.18.44", + "@types/node": "^20.4.6", "eslint": "^8.47.0", "eslint-plugin-playwright": "^0.15.3", "lodash": "^4.17.21", @@ -20,7 +18,7 @@ "typescript": "^5.1.6" }, "engines": { - "node": ">=16", + "node": ">=20", "yarn": ">=1.22.1 <2.0.0" }, "scripts": { diff --git a/e2e/src/openXchange.spec.ts b/e2e/src/openXchange.spec.ts index a136cef7..01ff5d01 100644 --- a/e2e/src/openXchange.spec.ts +++ b/e2e/src/openXchange.spec.ts @@ -149,12 +149,12 @@ test.describe('OpenXchange', () => { await expect( aliceElementWebPage.locateChatMessageInRoom( - /October 4, 2040, 10:30 – 11:00 AM GMT\+2/, + /October 4, 2040, 10:30\s–\s11:00\sAM GMT\+2/, ), ).toBeVisible(); await expect( aliceElementWebPage.locateChatMessageInRoom( - /\(previously: October 3, 2040, 10:30 – 11:00 AM GMT\+2\)/, + /\(previously: October 3, 2040, 10:30\s–\s11:00\sAM GMT\+2\)/, ), ).toBeVisible(); diff --git a/e2e/src/pages/elementWebPage.ts b/e2e/src/pages/elementWebPage.ts index a82ec027..cdefb45b 100644 --- a/e2e/src/pages/elementWebPage.ts +++ b/e2e/src/pages/elementWebPage.ts @@ -15,7 +15,6 @@ */ import { expect, FrameLocator, Locator, Page } from '@playwright/test'; -import fetch from 'cross-fetch'; import { Credentials, getElementWebUrl, getSynapseUrl } from '../util'; export type OpenIdToken = { diff --git a/e2e/src/recurringMeetings.spec.ts b/e2e/src/recurringMeetings.spec.ts index 32ec578b..873f9e4b 100644 --- a/e2e/src/recurringMeetings.spec.ts +++ b/e2e/src/recurringMeetings.spec.ts @@ -233,13 +233,13 @@ test.describe('Recurring Meetings', () => { await expect( aliceElementWebPage.locateChatMessageInRoom( - /A single meeting from a meeting series is moved to October 9, 2040, 10:40 – 11:40 AM GMT\+2/, + /A single meeting from a meeting series is moved to October 9, 2040, 10:40\s–\s11:40\sAM GMT\+2/, ), ).toBeVisible(); await expect( aliceElementWebPage.locateChatMessageInRoom( - /\(previously: October 3, 2040, 10:30 – 11:30 AM GMT\+2/, + /\(previously: October 3, 2040, 10:30\s–\s11:30\sAM GMT\+2/, ), ).toBeVisible(); }); @@ -424,7 +424,7 @@ test.describe('Recurring Meetings', () => { await expect( aliceElementWebPage.locateChatMessageInRoom( - /A single meeting from a meeting series on October 3, 2040, 10:30 – 11:30 AM GMT\+2 is deleted/, + /A single meeting from a meeting series on October 3, 2040, 10:30\s–\s11:30\sAM GMT\+2 is deleted/, ), ).toBeVisible(); }); diff --git a/e2e/src/util/deactivateUser.ts b/e2e/src/util/deactivateUser.ts index ec448f65..a516df5f 100644 --- a/e2e/src/util/deactivateUser.ts +++ b/e2e/src/util/deactivateUser.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import fetch from 'cross-fetch'; import { getSynapseUrl } from './config'; import { User } from './registerUser'; diff --git a/e2e/src/util/registerUser.ts b/e2e/src/util/registerUser.ts index 09b6603c..1f4d304d 100644 --- a/e2e/src/util/registerUser.ts +++ b/e2e/src/util/registerUser.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import fetch from 'cross-fetch'; import { createHmac } from 'crypto'; import { getSynapseRegistrationSecret, getSynapseUrl } from './config'; diff --git a/matrix-meetings-bot/Dockerfile b/matrix-meetings-bot/Dockerfile index 1e59be30..1fcee986 100644 --- a/matrix-meetings-bot/Dockerfile +++ b/matrix-meetings-bot/Dockerfile @@ -1,11 +1,11 @@ -FROM node:16-bullseye AS node_modules +FROM node:20-bullseye AS node_modules WORKDIR /build COPY package.json yarn.lock ./ COPY matrix-meetings-bot/package.json ./matrix-meetings-bot/ COPY resolutions/matrix-sdk-crypto-nodejs ./resolutions/matrix-sdk-crypto-nodejs/ RUN yarn install --production --frozen-lockfile --network-timeout 1000000 -FROM node:16-bullseye-slim +FROM node:20-bullseye-slim ENV NODE_ENV=production WORKDIR /app RUN set -x\ diff --git a/matrix-meetings-bot/package.json b/matrix-meetings-bot/package.json index fc212512..71f6703f 100644 --- a/matrix-meetings-bot/package.json +++ b/matrix-meetings-bot/package.json @@ -27,7 +27,7 @@ "generate-disclaimer": "cp ../LICENSE ./lib/LICENSE.txt && cp NOTICE ./lib/NOTICE.txt && yarn licenses generate-disclaimer --prod >> ./lib/NOTICE.txt && yarn licenses list --prod --json --no-progress > ./lib/licenses.json" }, "engines": { - "node": ">=16", + "node": ">=20", "yarn": ">=1.22.1 <2.0.0" }, "dependencies": { @@ -53,7 +53,6 @@ "mime-types": "^2.1.35", "mustache": "^4.2.0", "nestjs-pino": "^3.5.0", - "node-fetch": "^2.7.0", "pino": "^8.16.0", "pino-http": "^8.5.0", "reflect-metadata": "^0.1.13", @@ -69,8 +68,7 @@ "@types/luxon": "^3.2.0", "@types/mime-types": "^2.1.1", "@types/mustache": "^4.2.2", - "@types/node": "^16.18.44", - "@types/node-fetch": "^2.6.4", + "@types/node": "^20.4.6", "@types/uuid": "^9.0.4", "copyfiles": "^2.4.1", "depcheck": "^1.4.5", diff --git a/matrix-meetings-bot/setupTests.ts b/matrix-meetings-bot/setupTests.ts index 37dbfdae..a41d48a0 100644 --- a/matrix-meetings-bot/setupTests.ts +++ b/matrix-meetings-bot/setupTests.ts @@ -37,6 +37,29 @@ i18next.use(i18nextBackend).init({ }, }); +const DateTimeFormat = Intl.DateTimeFormat; +beforeEach(() => { + jest.spyOn(Intl, 'DateTimeFormat').mockImplementation((locale, options) => { + const format = new DateTimeFormat(locale, options); + + // replace all uncommon whitespace characters with ' '. Relates to https://github.com/nodejs/node/pull/45068 + // where the unicode standard decided to use U+2009 in some cases. This breaks some of our tests + // @ts-ignore: DateTimeFormat#formatRange will be available in TypeScript >4.7.2 + const originalFormatRange = format.formatRange; + jest + // @ts-ignore: DateTimeFormat#formatRange will be available in TypeScript >4.7.2 + .spyOn(format, 'formatRange') + // @ts-ignore: DateTimeFormat#formatRange will be available in TypeScript >4.7.2 + .mockImplementation((startDate, endDate) => + originalFormatRange + .call(format, startDate, endDate) + .replace(/\s+/g, ' '), + ); + + return format; + }); +}); + registerDateRangeFormatter(i18next); // We want our tests to be in a reproducible time zone, always resulting in diff --git a/matrix-meetings-bot/src/client/JitsiClient.ts b/matrix-meetings-bot/src/client/JitsiClient.ts index 078c9714..07468bed 100644 --- a/matrix-meetings-bot/src/client/JitsiClient.ts +++ b/matrix-meetings-bot/src/client/JitsiClient.ts @@ -15,7 +15,6 @@ */ import { Inject, Injectable, Logger } from '@nestjs/common'; -import fetch from 'node-fetch'; import { base32 } from 'rfc4648'; import { IAppConfiguration } from '../IAppConfiguration'; import { ModuleProviderToken } from '../ModuleProviderToken'; diff --git a/matrix-meetings-bot/src/controller/HealthCheckController.ts b/matrix-meetings-bot/src/controller/HealthCheckController.ts index 0602a88b..4c490813 100644 --- a/matrix-meetings-bot/src/controller/HealthCheckController.ts +++ b/matrix-meetings-bot/src/controller/HealthCheckController.ts @@ -15,7 +15,6 @@ */ import { Controller, Get, HttpException, Inject } from '@nestjs/common'; -import fetch from 'node-fetch'; import { IAppConfiguration } from '../IAppConfiguration'; import { ModuleProviderToken } from '../ModuleProviderToken'; diff --git a/matrix-meetings-bot/src/middleware/MatrixAuthMiddleware.ts b/matrix-meetings-bot/src/middleware/MatrixAuthMiddleware.ts index 143d0741..61f59c06 100644 --- a/matrix-meetings-bot/src/middleware/MatrixAuthMiddleware.ts +++ b/matrix-meetings-bot/src/middleware/MatrixAuthMiddleware.ts @@ -17,7 +17,6 @@ import { Inject, Injectable, Logger, NestMiddleware } from '@nestjs/common'; import base64url from 'base64url'; import { NextFunction, Request, Response } from 'express'; -import fetch from 'node-fetch'; import { IAppConfiguration } from '../IAppConfiguration'; import { MatrixEndpoint } from '../MatrixEndpoint'; import { ModuleProviderToken } from '../ModuleProviderToken'; diff --git a/matrix-meetings-widget/package.json b/matrix-meetings-widget/package.json index 966320ed..268bed62 100644 --- a/matrix-meetings-widget/package.json +++ b/matrix-meetings-widget/package.json @@ -52,7 +52,7 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.2.0", "@types/mustache": "^4.2.2", - "@types/node": "^16.18.44", + "@types/node": "^20.4.6", "@types/react": "^17.0.53", "@types/react-dom": "^17.0.19", "@types/react-i18next": "^8.1.0", @@ -67,7 +67,7 @@ "typescript": "^5.1.6" }, "engines": { - "node": ">=16", + "node": ">=20", "yarn": ">=1.22.1 <2.0.0" }, "scripts": { diff --git a/matrix-meetings-widget/src/components/meetings/MeetingCardShareMeetingContent/useDownloadIcsFile.test.tsx b/matrix-meetings-widget/src/components/meetings/MeetingCardShareMeetingContent/useDownloadIcsFile.test.tsx index 73c811e6..e9fed580 100644 --- a/matrix-meetings-widget/src/components/meetings/MeetingCardShareMeetingContent/useDownloadIcsFile.test.tsx +++ b/matrix-meetings-widget/src/components/meetings/MeetingCardShareMeetingContent/useDownloadIcsFile.test.tsx @@ -92,7 +92,7 @@ describe('useDownloadIcsFile', () => { slice: jest.fn(), stream: jest.fn(), text: jest.fn(), - }); + } as unknown as Blob); (URL.createObjectURL as jest.Mock).mockReturnValue('blob:url'); diff --git a/matrix-meetings-widget/src/timezoneMockUtils.ts b/matrix-meetings-widget/src/timezoneMockUtils.ts index 8e41e710..12901230 100644 --- a/matrix-meetings-widget/src/timezoneMockUtils.ts +++ b/matrix-meetings-widget/src/timezoneMockUtils.ts @@ -20,13 +20,25 @@ import { Settings } from 'luxon'; const DateTimeFormat = Intl.DateTimeFormat; export function mockDateTimeFormatTimeZone(timeZone: string): void { - jest.spyOn(Intl, 'DateTimeFormat').mockImplementation( - (locale, options) => - new DateTimeFormat(locale, { - ...options, - timeZone: options?.timeZone ?? timeZone, - }), - ); + jest.spyOn(Intl, 'DateTimeFormat').mockImplementation((locale, options) => { + const format = new DateTimeFormat(locale, { + ...options, + timeZone: options?.timeZone ?? timeZone, + }); + + // replace all uncommon whitespace characters with ' '. Relates to https://github.com/nodejs/node/pull/45068 + // where the unicode standard decided to use U+2009 in some cases. This breaks some of our tests + const originalFormatRange = format.formatRange; + jest + .spyOn(format, 'formatRange') + .mockImplementation((startDate, endDate) => + originalFormatRange + .call(format, startDate, endDate) + .replace(/\s+/g, ' '), + ); + + return format; + }); // make sure getTimezoneOffset is based on the provided timezone and // not the system diff --git a/yarn.lock b/yarn.lock index 3f1b8ede..ab3cf9b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2234,12 +2234,7 @@ csstype "^3.1.2" prop-types "^15.8.1" -"@mui/types@^7.2.4": - version "7.2.4" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" - integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== - -"@mui/types@^7.2.5": +"@mui/types@^7.2.4", "@mui/types@^7.2.5": version "7.2.5" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.5.tgz#cd62a1fc5eb1044137ccab2053b431dd7cfc3cb8" integrity sha512-S2BwfNczr7VwS6ki8GoAXJyARoeSJDLuxOEPs3vEMyTALlf9PrdHv+sluX7kk3iKrCg/ML2mIWwapZvWbkMCQA== @@ -2995,18 +2990,12 @@ resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.2.2.tgz#825bf5c214c3ab84d0b23fef2c8eb898f3ff8717" integrity sha512-MUSpfpW0yZbTgjekDbH0shMYBUD+X/uJJJMm9LXN1d5yjl5lCY1vN/eWKD6D1tOtjA6206K0zcIPnUaFMurdNA== -"@types/node-fetch@^2.6.4": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" - integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== +"@types/node@*", "@types/node@^20.4.6": + version "20.8.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.7.tgz#ad23827850843de973096edfc5abc9e922492a25" + integrity sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ== dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*", "@types/node@^16.18.44": - version "16.18.44" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.44.tgz#3c3ea2a832014b869f0f370630d98bb026171e76" - integrity sha512-PZXtT+wqSMHnLPVExTh+tMt1VK+GvjRLsGZMbcQ4Mb/cG63xJig/TUmgrDa9aborl2i22UnpIzHYNu7s97NbBQ== + undici-types "~5.25.1" "@types/node@^12.7.1": version "12.20.52" @@ -5036,13 +5025,6 @@ cross-fetch@3.1.6, cross-fetch@^3.0.4: dependencies: node-fetch "^2.6.11" -cross-fetch@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" - integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== - dependencies: - node-fetch "^2.6.12" - cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -9498,7 +9480,7 @@ node-emoji@1.11.0: dependencies: lodash "^4.17.21" -node-fetch@^2.6.1, node-fetch@^2.6.11, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.7.0: +node-fetch@^2.6.1, node-fetch@^2.6.11, node-fetch@^2.6.12, node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -13167,6 +13149,11 @@ underscore.string@~3.3.4: sprintf-js "^1.0.3" util-deprecate "^1.0.2" +undici-types@~5.25.1: + version "5.25.3" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3" + integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"