diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index eac5b86e..7d1be2a9 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v4 - name: Log in to container registry - uses: docker/login-action@3d58c274f17dffee475a5520cbe67f0a882c4dbb + uses: docker/login-action@83a00bc1ab5ded6580f31df1c49e6aaa932d840d with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -54,7 +54,7 @@ jobs: tar --numeric-owner --transform "s/dist/element-call-${TARBALL_VERSION}/" -cvzf element-call-${TARBALL_VERSION}.tar.gz dist - name: Upload - uses: actions/upload-artifact@4c0ff1c489dca52fedb26375d7d8fe7bd9233f19 + uses: actions/upload-artifact@ef09cdac3e2d3e60d8ccadda691f4f1cec5035cb env: GITHUB_TOKEN: ${{ github.token }} with: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d37aa75c..85385cb5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,11 +1,11 @@ -name: Run jest tests +name: Run unit tests on: pull_request: {} push: branches: [livekit, full-mesh] jobs: - jest: - name: Run jest tests + vitest: + name: Run vitest tests runs-on: ubuntu-latest steps: - name: Checkout code @@ -16,7 +16,7 @@ jobs: cache: "yarn" - name: Install dependencies run: "yarn install" - - name: Jest + - name: Vitest run: "yarn run test" - name: Upload to codecov uses: codecov/codecov-action@v4 diff --git a/package.json b/package.json index 0cb8ede6..06a3bc1a 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,14 @@ "lint:types": "tsc", "i18n": "node_modules/i18next-parser/bin/cli.js", "i18n:check": "node_modules/i18next-parser/bin/cli.js --fail-on-warnings --fail-on-update", - "test": "jest", + "test": "vitest", + "test:coverage": "vitest run --coverage", "backend": "docker-compose -f backend-docker-compose.yml up" }, "dependencies": { "@juggle/resize-observer": "^3.3.1", - "@livekit/components-core": "^0.8.0", - "@livekit/components-react": "^1.1.0", + "@livekit/components-core": "^0.9.0", + "@livekit/components-react": "^2.0.0", "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz", "@opentelemetry/api": "^1.4.0", "@opentelemetry/context-zone": "^1.9.1", @@ -59,7 +60,7 @@ "i18next": "^23.0.0", "i18next-browser-languagedetector": "^7.0.0", "i18next-http-backend": "^2.0.0", - "livekit-client": "1.15.12", + "livekit-client": "^2.0.2", "lodash": "^4.17.21", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#99600e87f193508ad56325baf2bb134110f3905a", "matrix-widget-api": "^1.3.1", @@ -88,21 +89,19 @@ "@react-spring/rafz": "^9.7.3", "@react-types/dialog": "^3.5.5", "@sentry/vite-plugin": "^2.0.0", - "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@types/content-type": "^1.1.5", "@types/dom-screen-wake-lock": "^1.0.1", "@types/dompurify": "^3.0.2", "@types/grecaptcha": "^3.0.4", - "@types/jest": "^29.5.5", "@types/node": "^20.0.0", "@types/react-router-dom": "^5.3.3", "@types/request": "^2.48.8", "@types/sdp-transform": "^2.4.5", "@types/uuid": "9", - "@typescript-eslint/eslint-plugin": "^6.1.0", - "@typescript-eslint/parser": "^6.1.0", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "babel-loader": "^9.0.0", "babel-plugin-transform-vite-meta-env": "^1.0.3", "eslint": "^8.14.0", @@ -116,37 +115,14 @@ "eslint-plugin-react-hooks": "^4.5.0", "eslint-plugin-unicorn": "^51.0.0", "i18next-parser": "^8.0.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^29.2.2", - "jest-environment-jsdom": "^29.3.1", - "jest-mock": "^29.5.0", + "jsdom": "^24.0.0", "prettier": "^3.0.0", "sass": "^1.42.1", "typescript": "^5.1.6", "typescript-eslint-language-service": "^5.0.5", "vite": "^5.0.0", "vite-plugin-html-template": "^1.1.0", - "vite-plugin-svgr": "^4.0.0" - }, - "jest": { - "testEnvironment": "./test/environment.ts", - "testMatch": [ - "/test/**/*-test.[jt]s?(x)" - ], - "transformIgnorePatterns": [ - "/node_modules/(?!d3)+$", - "/node_modules/(?!internmap)+$" - ], - "moduleNameMapper": { - "\\.css$": "identity-obj-proxy", - "\\.svg\\?react$": "/test/mocks/svgr.ts", - "^\\./IndexedDBWorker\\?worker$": "/test/mocks/workerMock.ts", - "^\\./olm$": "/test/mocks/olmMock.ts" - }, - "collectCoverage": true, - "coverageReporters": [ - "text", - "cobertura" - ] + "vite-plugin-svgr": "^4.0.0", + "vitest": "^1.2.2" } } diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 0acb3006..a1eeec67 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -143,7 +143,7 @@ "unmute_microphone_button_label": "Unmute microphone", "version": "Version: {{version}}", "video_tile": { - "change_fit_contain": "Crop to fit", + "change_fit_contain": "Fit to frame", "exit_full_screen": "Exit full screen", "full_screen": "Full screen", "mute_for_me": "Mute for me", diff --git a/renovate.json b/renovate.json index 548d9e50..8e4e5b9c 100644 --- a/renovate.json +++ b/renovate.json @@ -8,6 +8,7 @@ "enabled": false } ], + "semanticCommits": "disabled", "ignoreDeps": [ "@react-aria/button", "@react-aria/focus", diff --git a/src/livekit/options.ts b/src/livekit/options.ts index 00f19b12..5a8d4f10 100644 --- a/src/livekit/options.ts +++ b/src/livekit/options.ts @@ -59,5 +59,4 @@ export const defaultLiveKitOptions: RoomOptions = { stopLocalTrackOnUnpublish: true, reconnectPolicy: new DefaultReconnectPolicy(), disconnectOnPageLeave: true, - expWebAudioMix: false, }; diff --git a/src/livekit/useECConnectionState.ts b/src/livekit/useECConnectionState.ts index 17baf124..1ca3b675 100644 --- a/src/livekit/useECConnectionState.ts +++ b/src/livekit/useECConnectionState.ts @@ -61,7 +61,9 @@ async function doConnect( // doesn't publish it until you unmute. We want to publish it from the start so we're // always capturing audio: it helps keep bluetooth headsets in the right mode and // mobile browsers to know we're doing a call. - if (livekitRoom!.localParticipant.getTrack(Track.Source.Microphone)) { + if ( + livekitRoom!.localParticipant.getTrackPublication(Track.Source.Microphone) + ) { logger.warn( "Pre-creating audio track but participant already appears to have an microphone track: this shouldn't happen!", ); @@ -90,7 +92,9 @@ async function doConnect( if (!audioEnabled) await preCreatedAudioTrack?.mute(); // check again having awaited for the track to create - if (livekitRoom!.localParticipant.getTrack(Track.Source.Microphone)) { + if ( + livekitRoom!.localParticipant.getTrackPublication(Track.Source.Microphone) + ) { logger.warn( "Pre-created audio track but participant already appears to have an microphone track: this shouldn't happen!", ); @@ -174,7 +178,7 @@ export function useECConnectionState( const doFocusSwitch = useCallback(async (): Promise => { const screenshareTracks: MediaStreamTrack[] = []; - for (const t of livekitRoom!.localParticipant.videoTracks.values()) { + for (const t of livekitRoom!.localParticipant.videoTrackPublications.values()) { if (t.track && t.source == Track.Source.ScreenShare) { const newTrack = t.track.mediaStreamTrack.clone(); newTrack.enabled = true; diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index fb7a4297..f6858693 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -291,7 +291,7 @@ export function useLiveKit( room.options.audioCaptureDefaults?.deviceId === "default" ) { const activeMicTrack = Array.from( - room.localParticipant.audioTracks.values(), + room.localParticipant.audioTrackPublications.values(), ).find((d) => d.source === Track.Source.Microphone)?.track; const defaultDevice = device.available.find( @@ -313,7 +313,7 @@ export function useLiveKit( // Note that room.switchActiveDevice() won't work: Livekit will ignore it because // the deviceId hasn't changed (was & still is default). room.localParticipant - .getTrack(Track.Source.Microphone) + .getTrackPublication(Track.Source.Microphone) ?.audioTrack?.restartTrack(); } } else { diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index da6c38b1..f5025710 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -188,17 +188,9 @@ export async function initClient( await client.store.startup(); } - if (client.initCrypto) { - await client.initCrypto(); - client.setGlobalErrorOnUnknownDevices(false); - } - - await client.startClient({ - // dirty hack to reduce chance of gappy syncs - // should be fixed by spotting gaps and backpaginating - initialSyncLimit: 50, - }); - + await client.initCrypto(); + client.setGlobalErrorOnUnknownDevices(false); + await client.startClient(); await waitForSync(client); return client; diff --git a/src/room/RoomAuthView.tsx b/src/room/RoomAuthView.tsx index 2b17e54a..272b82e4 100644 --- a/src/room/RoomAuthView.tsx +++ b/src/room/RoomAuthView.tsx @@ -41,6 +41,8 @@ export const RoomAuthView: FC = () => { // @ts-ignore (e) => { e.preventDefault(); + setLoading(true); + const data = new FormData(e.target); const dataForDisplayName = data.get("displayName"); const displayName = diff --git a/src/state/TileViewModel.ts b/src/state/TileViewModel.ts index b059c42c..59c48bdc 100644 --- a/src/state/TileViewModel.ts +++ b/src/state/TileViewModel.ts @@ -56,7 +56,7 @@ function observeTrackReference( observeParticipantMedia(participant).pipe( map(() => ({ participant, - publication: participant.getTrack(source), + publication: participant.getTrackPublication(source), source, })), distinctUntilKeyChanged("publication"), diff --git a/test/Toast-test.tsx b/test/Toast-test.tsx index 88cfbf4b..6b573523 100644 --- a/test/Toast-test.tsx +++ b/test/Toast-test.tsx @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { vi } from "vitest"; import { screen, render } from "@testing-library/react"; import { Toast } from "../src/Toast"; import userEvent from "@testing-library/user-event"; @@ -35,7 +36,7 @@ test("Toast renders", () => { }); test("Toast dismisses when clicked", async () => { - const onDismiss = jest.fn(); + const onDismiss = vi.fn(); render( Hello world! @@ -47,13 +48,13 @@ test("Toast dismisses when clicked", async () => { test("Toast dismisses itself after the specified timeout", async () => { withFakeTimers(() => { - const onDismiss = jest.fn(); + const onDismiss = vi.fn(); render( Hello world! , ); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(onDismiss).toHaveBeenCalled(); }); }); diff --git a/test/UrlParams-test.ts b/test/UrlParams-test.ts index e0eb7c29..eb03d976 100644 --- a/test/UrlParams-test.ts +++ b/test/UrlParams-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from "jest-mock"; +import { vi } from "vitest"; import { getRoomIdentifierFromUrl } from "../src/UrlParams"; import { Config } from "../src/config/Config"; @@ -24,11 +24,11 @@ const ROOM_ID = "!d45f138fsd"; const ORIGIN = "https://call.element.io"; const HOMESERVER = "call.ems.host"; -jest.mock("../src/config/Config"); +vi.mock("../src/config/Config"); describe("UrlParams", () => { beforeAll(() => { - mocked(Config.defaultServerName).mockReturnValue("call.ems.host"); + vi.mocked(Config.defaultServerName).mockReturnValue("call.ems.host"); }); describe("handles URL with /room/", () => { diff --git a/test/__snapshots__/Toast-test.tsx.snap b/test/__snapshots__/Toast-test.tsx.snap index 64822136..7edfdcf7 100644 --- a/test/__snapshots__/Toast-test.tsx.snap +++ b/test/__snapshots__/Toast-test.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Toast renders 1`] = `