diff --git a/.eslintrc.js b/.eslintrc.js index f0720a0252..342dbde484 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -169,7 +169,7 @@ module.exports = { }, overrides: [ { - files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts", "playwright/**/*.ts"], + files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "playwright/**/*.ts"], extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"], rules: { "@typescript-eslint/explicit-function-return-type": [ @@ -233,14 +233,14 @@ module.exports = { }, }, { - files: ["test/**/*.{ts,tsx}", "cypress/**/*.ts", "playwright/**/*.ts"], + files: ["test/**/*.{ts,tsx}", "playwright/**/*.ts"], extends: ["plugin:matrix-org/jest"], rules: { // We don't need super strict typing in test utilities "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", - // Jest/Cypress specific + // Jest/Playwright specific // Disabled tests are a reality for now but as soon as all of the xits are // eliminated, we should enforce this. @@ -255,29 +255,11 @@ module.exports = { ], }, }, - { - files: ["cypress/**/*.ts"], - parserOptions: { - project: ["./cypress/tsconfig.json"], - }, - rules: { - // Cypress "promises" work differently - disable some related rules - "jest/valid-expect": "off", - "jest/valid-expect-in-promise": "off", - "jest/no-done-callback": "off", - }, - }, { files: ["playwright/**/*.ts"], parserOptions: { project: ["./playwright/tsconfig.json"], }, - rules: { - // Cypress "promises" work differently - disable some related rules - "jest/valid-expect": "off", - "jest/valid-expect-in-promise": "off", - "jest/no-done-callback": "off", - }, }, ], settings: { diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml deleted file mode 100644 index b0100fcd8d..0000000000 --- a/.github/workflows/cypress.yaml +++ /dev/null @@ -1,231 +0,0 @@ -# Triggers after the layered build has finished, taking the artifact and running cypress on it -# -# Also called by a workflow in matrix-js-sdk. -# -name: Cypress End to End Tests -on: - workflow_run: - workflows: ["Element Web - Build"] - types: - - completed - - # support calls from other workflows - workflow_call: - inputs: - react-sdk-repository: - type: string - required: true - description: "The name of the github repository to check out and build." - secrets: - KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS_RUST: - required: true - KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS_LEGACY: - required: true - TCMS_USERNAME: - required: true - TCMS_PASSWORD: - required: true - -concurrency: - group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }} - cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }} - -jobs: - prepare: - name: Prepare - if: github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-latest - permissions: - actions: read - issues: read - statuses: write - pull-requests: read - outputs: - uuid: ${{ steps.uuid.outputs.value }} - pr_id: ${{ steps.prdetails.outputs.pr_id }} - percy_enable: ${{ steps.percy.outputs.value || '0' }} - steps: - # We create the status here and then update it to success/failure in the `report` stage - # This provides an easy link to this workflow_run from the PR before Cypress is done. - - uses: Sibz/github-status-action@071b5370da85afbb16637d6eed8524a06bc2053e # v1 - with: - authToken: ${{ secrets.GITHUB_TOKEN }} - state: pending - context: ${{ github.workflow }} / cypress - sha: ${{ github.event.workflow_run.head_sha }} - target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - - - id: prdetails - if: github.event.workflow_run.event == 'pull_request' || github.event.workflow_run.event == 'merge_group' - uses: matrix-org/pr-details-action@v1.3 - with: - owner: ${{ github.event.workflow_run.head_repository.owner.login }} - branch: ${{ github.event.workflow_run.head_branch }} - - # Percy is disabled while we're figuring out https://github.com/vector-im/wat-internal/issues/36 - # and https://github.com/vector-im/wat-internal/issues/56. We're hoping to turn it back on or switch - # to an alternative in the future. - # # Only run Percy when it is demanded or we are running the daily build - # - name: Enable Percy - # id: percy - # if: | - # github.event.workflow_run.event == 'schedule' || - # ( - # github.event.workflow_run.event == 'merge_group' && - # contains(fromJSON(steps.prdetails.outputs.data).labels.*.name, 'X-Needs-Percy') - # ) - # run: echo "value=1" >> $GITHUB_OUTPUT - - - name: Generate unique ID 💎 - id: uuid - run: echo "value=sha-$GITHUB_SHA-time-$(date +"%s")" >> $GITHUB_OUTPUT - - tests: - name: "Run Tests (${{ matrix.crypto }} crypto)" - needs: prepare - runs-on: ubuntu-latest - permissions: - actions: read - issues: read - pull-requests: read - environment: Cypress - strategy: - fail-fast: false - matrix: - # Run tests using both crypto stacks - crypto: [legacy, rust] - ci_node_total: [2] - ci_node_index: [0, 1] - steps: - # The version of chrome shipped by default may not be consistent across runners - # so we explicitly use a specific version of chrome here. - - uses: browser-actions/setup-chrome@803ef6dfb4fdf22089c9563225d95e4a515820a0 # v1 - - run: echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV - - # There's a 'download artifact' action, but it hasn't been updated for the workflow_run action - # (https://github.com/actions/download-artifact/issues/60) so instead we get this mess: - - name: 📥 Download artifact - uses: dawidd6/action-download-artifact@f29d1b6a8930683e80acedfbe6baa2930cd646b4 # v2 - with: - run_id: ${{ github.event.workflow_run.id }} - name: previewbuild - path: webapp - - # The workflow_run.head_sha is the sha of the head commit but the element-web was built using a simulated - # merge commit - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request - # so use the sha from the tarball for the checkout of the cypress tests - # to make sure we get a matching set of code and tests. - - name: Grab sha from webapp - id: sha - run: | - echo "sha=$(cat webapp/sha)" >> $GITHUB_OUTPUT - - - uses: actions/checkout@v4 - with: - # XXX: We're checking out untrusted code in a secure context - # We need to be careful to not trust anything this code outputs/may do - # - # Note that (in the absence of a `react-sdk-repository` input), - # we check out from the default repository, which is (for this workflow) the - # *target* repository for the pull request. - # - ref: ${{ steps.sha.outputs.sha }} - persist-credentials: false - path: matrix-react-sdk - repository: ${{ inputs.react-sdk-repository || github.repository }} - - # Enable rust crypto if the calling workflow requests it - - name: Enable rust crypto - if: matrix.crypto == 'rust' - run: | - echo "CYPRESS_RUST_CRYPTO=1" >> "$GITHUB_ENV" - - - name: Run Cypress tests via knapsack pro - uses: cypress-io/github-action@ebe8b24c4428922d0f793a5c4c96853a633180e3 # v6.6.0 - with: - working-directory: matrix-react-sdk - headed: true - start: npx serve -p 8080 -L ../webapp - wait-on: "http://localhost:8080" - record: false - parallel: false - # The built-in Electron runner seems to grind to a halt trying to run the tests, so use chrome. - command: yarn percy exec --parallel -- npx knapsack-pro-cypress --config trashAssetsBeforeRuns=false --browser ${{ env.BROWSER_PATH }} - env: - # Knapsack token and config - KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS: ${{ matrix.crypto == 'rust' && secrets.KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS_RUST || secrets.KNAPSACK_PRO_TEST_SUITE_TOKEN_CYPRESS_LEGACY }} - KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }} - KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }} - KNAPSACK_PRO_TEST_FILE_PATTERN: cypress/e2e/**/*.spec.ts - KNAPSACK_PRO_BRANCH: ${{ github.event.workflow_run.head_branch }} - KNAPSACK_PRO_COMMIT_HASH: ${{ github.event.workflow_run.head_sha }} - - # Use existing chromium rather than downloading another - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - - # pass GitHub token to allow accurately detecting a build vs a re-run build - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # make Node's os.tmpdir() return something where we actually have permissions - TMPDIR: ${{ runner.temp }} - - # pass the Percy token as an environment variable - PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} - - # only run percy on legacy crypto (for now) - PERCY_ENABLE: ${{ matrix.crypto == 'legacy' && needs.prepare.outputs.percy_enable || 0 }} - PERCY_BROWSER_EXECUTABLE: ${{ steps.setup-chrome.outputs.chrome-path }} - # tell Percy more details about the context of this run - PERCY_BRANCH: ${{ github.event.workflow_run.head_branch }} - PERCY_COMMIT: ${{ github.event.workflow_run.head_sha }} - PERCY_PULL_REQUEST: ${{ needs.prepare.outputs.pr_id }} - PERCY_PARALLEL_NONCE: ${{ needs.prepare.outputs.uuid }} - # We manually finalize the build in the report stage - PERCY_PARALLEL_TOTAL: -1 - - - name: 📤 Upload results artifact - if: failure() - uses: actions/upload-artifact@v3 - with: - name: cypress-results-${{ matrix.crypto }}-crypto - path: | - matrix-react-sdk/cypress/screenshots - matrix-react-sdk/cypress/videos - matrix-react-sdk/cypress/synapselogs - matrix-react-sdk/cypress/results/cypresslogs - - report: - name: Finalize results - needs: - - prepare - - tests - runs-on: ubuntu-latest - if: always() - permissions: - statuses: write - steps: - - name: Finalize Percy - if: needs.prepare.outputs.percy_enable == '1' - run: npx -p @percy/cli percy build:finalize - env: - PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} - PERCY_PARALLEL_NONCE: ${{ needs.prepare.outputs.uuid }} - - - name: Skip Percy required check - if: needs.prepare.outputs.percy_enable != '1' - uses: Sibz/github-status-action@071b5370da85afbb16637d6eed8524a06bc2053e # v1 - with: - authToken: ${{ secrets.GITHUB_TOKEN }} - state: success - description: Percy skipped - context: percy/matrix-react-sdk - sha: ${{ github.event.workflow_run.head_sha }} - target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - - - uses: Sibz/github-status-action@071b5370da85afbb16637d6eed8524a06bc2053e # v1 - with: - authToken: ${{ secrets.GITHUB_TOKEN }} - state: ${{ needs.tests.result == 'success' && 'success' || 'failure' }} - context: ${{ github.workflow }} / cypress - sha: ${{ github.event.workflow_run.head_sha }} - target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/element-web.yaml b/.github/workflows/element-web.yaml index 0582ff4e52..de082badb2 100644 --- a/.github/workflows/element-web.yaml +++ b/.github/workflows/element-web.yaml @@ -3,12 +3,6 @@ # as an artifact and run integration tests. name: Element Web - Build on: - # We only need the nightly run for Percy which is disabled while we're - # figuring out https://github.com/vector-im/wat-internal/issues/36 and - # https://github.com/vector-im/wat-internal/issues/56. We're hoping to - # turn it back on or switch to an alternative in the future. - # schedule: - # - cron: "17 4 * * 1-5" # every weekday at 04:17 UTC pull_request: {} merge_group: types: [checks_requested] @@ -82,7 +76,7 @@ jobs: echo $VERSION > webapp/version working-directory: ./element-web - # Record the react-sdk sha so our cypress tests are from the same sha + # Record the react-sdk sha so our Playwright tests are from the same sha - name: Record react-sdk SHA run: | git rev-parse HEAD > element-web/webapp/sha diff --git a/.gitignore b/.gitignore index ecab8be95f..3137cd555b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,14 +19,3 @@ package-lock.json .vscode .vscode/ - -/cypress/videos -/cypress/downloads -/cypress/screenshots -/cypress/synapselogs -/cypress/dendritelogs -/cypress/results - -# These could have files in them but don't currently -# Cypress will still auto-create them though... -/cypress/performance diff --git a/.percy.yml b/.percy.yml deleted file mode 100644 index 7823892423..0000000000 --- a/.percy.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -snapshot: - widths: - - 1024 - - 1920 -percy: - defer-uploads: true diff --git a/README.md b/README.md index 67ab61a93d..f3f34939a9 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ ![Tests](https://github.com/matrix-org/matrix-react-sdk/actions/workflows/tests.yml/badge.svg) [![Playwright](https://img.shields.io/badge/Playwright-end_to_end_tests-blue)](https://e2e-develop--matrix-react-sdk.netlify.app/) ![Static Analysis](https://github.com/matrix-org/matrix-react-sdk/actions/workflows/static_analysis.yaml/badge.svg) -[![Knapsack Pro Parallel CI builds for Cypress Test - Legacy Crypto](https://img.shields.io/badge/Knapsack%20Pro-Parallel%20%2F%20Cypress%20Test%20--%20Legacy%20Crypto-%230074ff)](https://knapsackpro.com/dashboard/organizations/3882/projects/2469/test_suites/3724/builds?utm_campaign=organization-id-3882&utm_content=test-suite-id-3724&utm_medium=readme&utm_source=knapsack-pro-badge&utm_term=project-id-2469) -[![Knapsack Pro Parallel CI builds for Cypress Test - Rust Crypto](https://img.shields.io/badge/Knapsack%20Pro-Parallel%20%2F%20Cypress%20Test%20--%20Rust%20Crypto-%230074ff)](https://knapsackpro.com/dashboard/organizations/3882/projects/2469/test_suites/3729/builds?utm_campaign=organization-id-3882&utm_content=test-suite-id-3729&utm_medium=readme&utm_source=knapsack-pro-badge&utm_term=project-id-2469) [![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement-web%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element-web) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=matrix-react-sdk&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=matrix-react-sdk) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=matrix-react-sdk&metric=coverage)](https://sonarcloud.io/summary/new_code?id=matrix-react-sdk) @@ -206,5 +204,5 @@ Now the yarn commands should work as normal. ### End-to-End tests -We use Cypress and Element Web for end-to-end tests. See -[`docs/cypress.md`](docs/cypress.md) for more information. +We use Playwright and Element Web for end-to-end tests. See +[`docs/playwright.md`](docs/playwright.md) for more information. diff --git a/cypress-ci-reporter-config.json b/cypress-ci-reporter-config.json deleted file mode 100644 index e9f1330a6f..0000000000 --- a/cypress-ci-reporter-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "reporterEnabled": "spec, mocha-junit-reporter", - "mochaJunitReporterReporterOptions": { - "mochaFile": "cypress/results/junit/results-[hash].xml", - "useFullSuiteTitle": true - } -} diff --git a/cypress.config.ts b/cypress.config.ts deleted file mode 100644 index 5351e6d686..0000000000 --- a/cypress.config.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { defineConfig } from "cypress"; - -import registerPlugins from "./cypress/plugins"; - -export default defineConfig({ - videoUploadOnPasses: false, - projectId: "ppvnzg", - experimentalInteractiveRunEvents: true, - experimentalMemoryManagement: true, - defaultCommandTimeout: 10000, - chromeWebSecurity: false, - e2e: { - setupNodeEvents(on, config) { - return registerPlugins(on, config); - }, - baseUrl: "http://localhost:8080", - specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}", - }, - env: { - // Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image. - SLIDING_SYNC_PROXY_TAG: "v0.99.3", - HOMESERVER: "synapse", - }, - retries: { - runMode: 4, - openMode: 0, - }, - - // disable logging of HTTP requests made to the Cypress server. They are noisy and not very helpful. - // @ts-ignore https://github.com/cypress-io/cypress/issues/26284 - morgan: false, - - // Create XML result files - reporter: "cypress-multi-reporters", - reporterOptions: { - configFile: "cypress-ci-reporter-config.json", - }, -}); diff --git a/cypress/e2e/dummy.spec.ts b/cypress/e2e/dummy.spec.ts deleted file mode 100644 index 6940c642b5..0000000000 --- a/cypress/e2e/dummy.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2024 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -it("Dummy test to make CI pass", () => {}); diff --git a/cypress/e2e/utils.ts b/cypress/e2e/utils.ts deleted file mode 100644 index 0ffb125dae..0000000000 --- a/cypress/e2e/utils.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2023 Mikhail Aheichyk -Copyright 2023 Nordeck IT + Consulting GmbH. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import type { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; - -/** - * Resolves when room state matches predicate. - * @param win window object - * @param matrixClient MatrixClient instance that can be user or bot - * @param roomId room id to find room and check - * @param predicate defines condition that is used to check the room state - */ -export function waitForRoom( - win: Cypress.AUTWindow, - matrixClient: MatrixClient, - roomId: string, - predicate: (room: Room) => boolean, -): Promise { - return new Promise((resolve, reject) => { - const room = matrixClient.getRoom(roomId); - - if (predicate(room)) { - resolve(); - return; - } - - function onEvent(ev: MatrixEvent) { - if (ev.getRoomId() !== roomId) return; - - if (predicate(room)) { - matrixClient.removeListener(win.matrixcs.ClientEvent.Event, onEvent); - resolve(); - } - } - - matrixClient.on(win.matrixcs.ClientEvent.Event, onEvent); - }); -} diff --git a/cypress/fixtures/riot.png b/cypress/fixtures/riot.png deleted file mode 100644 index ee42954c78..0000000000 Binary files a/cypress/fixtures/riot.png and /dev/null differ diff --git a/cypress/global.d.ts b/cypress/global.d.ts deleted file mode 100644 index 330b66f6db..0000000000 --- a/cypress/global.d.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// eslint-disable-next-line no-restricted-imports -import "matrix-js-sdk/src/@types/global"; -import type { - MatrixClient, - ClientEvent, - MatrixScheduler, - MemoryCryptoStore, - MemoryStore, - Preset, - RoomStateEvent, - Visibility, - RoomMemberEvent, - ICreateClientOpts, -} from "matrix-js-sdk/src/matrix"; -import type { SettingLevel } from "../src/settings/SettingLevel"; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - interface ApplicationWindow { - // XXX: Importing SettingsStore causes a bunch of type lint errors - mxSettingsStore: { - setValue(settingName: string, roomId: string | null, level: SettingLevel, value: any): Promise; - }; - mxMatrixClientPeg: { - matrixClient?: MatrixClient; - }; - beforeReload?: boolean; // for detecting reloads - // Partial type for the matrix-js-sdk module, exported by browser-matrix - matrixcs: { - MatrixClient: typeof MatrixClient; - ClientEvent: typeof ClientEvent; - RoomMemberEvent: typeof RoomMemberEvent; - RoomStateEvent: typeof RoomStateEvent; - MatrixScheduler: typeof MatrixScheduler; - MemoryStore: typeof MemoryStore; - MemoryCryptoStore: typeof MemoryCryptoStore; - Visibility: typeof Visibility; - Preset: typeof Preset; - createClient(opts: ICreateClientOpts | string); - }; - } - } -} - -export { MatrixClient }; diff --git a/cypress/plugins/dendritedocker/index.ts b/cypress/plugins/dendritedocker/index.ts deleted file mode 100644 index f89f898121..0000000000 --- a/cypress/plugins/dendritedocker/index.ts +++ /dev/null @@ -1,207 +0,0 @@ -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import * as path from "path"; -import * as os from "os"; -import * as crypto from "crypto"; -import * as fse from "fs-extra"; - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; -import { getFreePort } from "../utils/port"; -import { dockerExec, dockerLogs, dockerRun, dockerStop } from "../docker"; -import { HomeserverConfig, HomeserverInstance } from "../utils/homeserver"; -import { StartHomeserverOpts } from "../../support/homeserver"; - -// A cypress plugins to add command to start & stop dendrites in -// docker with preset templates. - -const dendrites = new Map(); - -const dockerConfigDir = "/etc/dendrite/"; -const dendriteConfigFile = "dendrite.yaml"; - -function randB64Bytes(numBytes: number): string { - return crypto.randomBytes(numBytes).toString("base64").replace(/=*$/, ""); -} - -async function cfgDirFromTemplate(template: string, dendriteImage: string): Promise { - template = "default"; - const templateDir = path.join(__dirname, "templates", template); - - const stats = await fse.stat(templateDir); - if (!stats?.isDirectory) { - throw new Error(`No such template: ${template}`); - } - const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-dendritedocker-")); - - // copy the contents of the template dir, omitting homeserver.yaml as we'll template that - console.log(`Copy ${templateDir} -> ${tempDir}`); - await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== dendriteConfigFile }); - - const registrationSecret = randB64Bytes(16); - - const port = await getFreePort(); - const baseUrl = `http://localhost:${port}`; - - // now copy homeserver.yaml, applying substitutions - console.log(`Gen ${path.join(templateDir, dendriteConfigFile)}`); - let hsYaml = await fse.readFile(path.join(templateDir, dendriteConfigFile), "utf8"); - hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); - await fse.writeFile(path.join(tempDir, dendriteConfigFile), hsYaml); - - await dockerRun({ - image: dendriteImage, - params: ["--rm", "--entrypoint=", "-v", `${tempDir}:/mnt`], - containerName: `react-sdk-cypress-dendrite-keygen`, - cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"], - }); - - return { - port, - baseUrl, - configDir: tempDir, - registrationSecret, - }; -} - -// Start a dendrite instance: the template must be the name of -// one of the templates in the cypress/plugins/dendritedocker/templates -// directory -async function dendriteStart(opts: StartHomeserverOpts): Promise { - return containerStart(opts.template, false); -} - -// Start a dendrite instance using pinecone routing: the template must be the name of -// one of the templates in the cypress/plugins/dendritedocker/templates -// directory -async function dendritePineconeStart(template: string): Promise { - return containerStart(template, true); -} - -async function containerStart(template: string, usePinecone: boolean): Promise { - let dendriteImage = "matrixdotorg/dendrite-monolith:main"; - let dendriteEntrypoint = "/usr/bin/dendrite"; - if (usePinecone) { - dendriteImage = "matrixdotorg/dendrite-demo-pinecone:main"; - dendriteEntrypoint = "/usr/bin/dendrite-demo-pinecone"; - } - const denCfg = await cfgDirFromTemplate(template, dendriteImage); - - console.log(`Starting dendrite with config dir ${denCfg.configDir}...`); - - const dendriteId = await dockerRun({ - image: dendriteImage, - params: [ - "--rm", - "-v", - `${denCfg.configDir}:` + dockerConfigDir, - "-p", - `${denCfg.port}:8008/tcp`, - "--entrypoint", - dendriteEntrypoint, - ], - containerName: `react-sdk-cypress-dendrite`, - cmd: ["--config", dockerConfigDir + dendriteConfigFile, "--really-enable-open-registration", "true", "run"], - }); - - console.log(`Started dendrite with id ${dendriteId} on port ${denCfg.port}.`); - - // Await Dendrite healthcheck - await dockerExec({ - containerId: dendriteId, - params: [ - "curl", - "--connect-timeout", - "30", - "--retry", - "30", - "--retry-delay", - "1", - "--retry-all-errors", - "--silent", - "http://localhost:8008/_matrix/client/versions", - ], - }); - - const dendrite: HomeserverInstance = { serverId: dendriteId, ...denCfg }; - dendrites.set(dendriteId, dendrite); - return dendrite; -} - -async function dendriteStop(id: string): Promise { - const denCfg = dendrites.get(id); - - if (!denCfg) throw new Error("Unknown dendrite ID"); - - const dendriteLogsPath = path.join("cypress", "dendritelogs", id); - await fse.ensureDir(dendriteLogsPath); - - await dockerLogs({ - containerId: id, - stdoutFile: path.join(dendriteLogsPath, "stdout.log"), - stderrFile: path.join(dendriteLogsPath, "stderr.log"), - }); - - await dockerStop({ - containerId: id, - }); - - await fse.remove(denCfg.configDir); - - dendrites.delete(id); - - console.log(`Stopped dendrite id ${id}.`); - // cypress deliberately fails if you return 'undefined', so - // return null to signal all is well, and we've handled the task. - return null; -} - -async function dendritePineconeStop(id: string): Promise { - return dendriteStop(id); -} - -/** - * @type {Cypress.PluginConfig} - */ -export function dendriteDocker(on: PluginEvents, config: PluginConfigOptions) { - on("task", { - dendriteStart, - dendriteStop, - dendritePineconeStart, - dendritePineconeStop, - }); - - on("after:spec", async (spec) => { - // Cleans up any remaining dendrite instances after a spec run - // This is on the theory that we should avoid re-using dendrite - // instances between spec runs: they should be cheap enough to - // start that we can have a separate one for each spec run or even - // test. If we accidentally re-use dendrites, we could inadvertently - // make our tests depend on each other. - for (const denId of dendrites.keys()) { - console.warn(`Cleaning up dendrite ID ${denId} after ${spec.name}`); - await dendriteStop(denId); - } - }); - - on("before:run", async () => { - // tidy up old dendrite log files before each run - await fse.emptyDir(path.join("cypress", "dendritelogs")); - }); -} diff --git a/cypress/plugins/dendritedocker/templates/default/dendrite.yaml b/cypress/plugins/dendritedocker/templates/default/dendrite.yaml deleted file mode 100644 index 634cebbc87..0000000000 --- a/cypress/plugins/dendritedocker/templates/default/dendrite.yaml +++ /dev/null @@ -1,378 +0,0 @@ -# This is the Dendrite configuration file. -# -# The configuration is split up into sections - each Dendrite component has a -# configuration section, in addition to the "global" section which applies to -# all components. - -# The version of the configuration file. -version: 2 - -# Global Matrix configuration. This configuration applies to all components. -global: - # The domain name of this homeserver. - server_name: localhost - - # The path to the signing private key file, used to sign requests and events. - # Note that this is NOT the same private key as used for TLS! To generate a - # signing key, use "./bin/generate-keys --private-key matrix_key.pem". - private_key: matrix_key.pem - - # The paths and expiry timestamps (as a UNIX timestamp in millisecond precision) - # to old signing keys that were formerly in use on this domain name. These - # keys will not be used for federation request or event signing, but will be - # provided to any other homeserver that asks when trying to verify old events. - old_private_keys: - # If the old private key file is available: - # - private_key: old_matrix_key.pem - # expired_at: 1601024554498 - # If only the public key (in base64 format) and key ID are known: - # - public_key: mn59Kxfdq9VziYHSBzI7+EDPDcBS2Xl7jeUdiiQcOnM= - # key_id: ed25519:mykeyid - # expired_at: 1601024554498 - - # How long a remote server can cache our server signing key before requesting it - # again. Increasing this number will reduce the number of requests made by other - # servers for our key but increases the period that a compromised key will be - # considered valid by other homeservers. - key_validity_period: 168h0m0s - - # Global database connection pool, for PostgreSQL monolith deployments only. If - # this section is populated then you can omit the "database" blocks in all other - # sections. For polylith deployments, or monolith deployments using SQLite databases, - # you must configure the "database" block for each component instead. - # database: - # connection_string: postgresql://username:password@hostname/dendrite?sslmode=disable - # max_open_conns: 90 - # max_idle_conns: 5 - # conn_max_lifetime: -1 - - # Configuration for in-memory caches. Caches can often improve performance by - # keeping frequently accessed items (like events, identifiers etc.) in memory - # rather than having to read them from the database. - cache: - # The estimated maximum size for the global cache in bytes, or in terabytes, - # gigabytes, megabytes or kilobytes when the appropriate 'tb', 'gb', 'mb' or - # 'kb' suffix is specified. Note that this is not a hard limit, nor is it a - # memory limit for the entire process. A cache that is too small may ultimately - # provide little or no benefit. - max_size_estimated: 1gb - - # The maximum amount of time that a cache entry can live for in memory before - # it will be evicted and/or refreshed from the database. Lower values result in - # easier admission of new cache entries but may also increase database load in - # comparison to higher values, so adjust conservatively. Higher values may make - # it harder for new items to make it into the cache, e.g. if new rooms suddenly - # become popular. - max_age: 1h - - # The server name to delegate server-server communications to, with optional port - # e.g. localhost:443 - well_known_server_name: "" - - # The server name to delegate client-server communications to, with optional port - # e.g. localhost:443 - well_known_client_name: "" - - # Lists of domains that the server will trust as identity servers to verify third - # party identifiers such as phone numbers and email addresses. - trusted_third_party_id_servers: - - matrix.org - - vector.im - - # Disables federation. Dendrite will not be able to communicate with other servers - # in the Matrix federation and the federation API will not be exposed. - disable_federation: false - - # Configures the handling of presence events. Inbound controls whether we receive - # presence events from other servers, outbound controls whether we send presence - # events for our local users to other servers. - presence: - enable_inbound: false - enable_outbound: false - - # Configures phone-home statistics reporting. These statistics contain the server - # name, number of active users and some information on your deployment config. - # We use this information to understand how Dendrite is being used in the wild. - report_stats: - enabled: false - endpoint: https://matrix.org/report-usage-stats/push - - # Server notices allows server admins to send messages to all users on the server. - server_notices: - enabled: false - # The local part, display name and avatar URL (as a mxc:// URL) for the user that - # will send the server notices. These are visible to all users on the deployment. - local_part: "_server" - display_name: "Server Alerts" - avatar_url: "" - # The room name to be used when sending server notices. This room name will - # appear in user clients. - room_name: "Server Alerts" - - # Configuration for NATS JetStream - jetstream: - # A list of NATS Server addresses to connect to. If none are specified, an - # internal NATS server will be started automatically when running Dendrite in - # monolith mode. For polylith deployments, it is required to specify the address - # of at least one NATS Server node. - addresses: - # - localhost:4222 - - # Disable the validation of TLS certificates of NATS. This is - # not recommended in production since it may allow NATS traffic - # to be sent to an insecure endpoint. - disable_tls_validation: false - - # Persistent directory to store JetStream streams in. This directory should be - # preserved across Dendrite restarts. - storage_path: ./ - - # The prefix to use for stream names for this homeserver - really only useful - # if you are running more than one Dendrite server on the same NATS deployment. - topic_prefix: Dendrite - - # Configuration for Prometheus metric collection. - metrics: - enabled: false - basic_auth: - username: metrics - password: metrics - - # Optional DNS cache. The DNS cache may reduce the load on DNS servers if there - # is no local caching resolver available for use. - dns_cache: - enabled: false - cache_size: 256 - cache_lifetime: "5m" # 5 minutes; https://pkg.go.dev/time@master#ParseDuration - -# Configuration for the Appservice API. -app_service_api: - # Disable the validation of TLS certificates of appservices. This is - # not recommended in production since it may allow appservice traffic - # to be sent to an insecure endpoint. - disable_tls_validation: false - - # Appservice configuration files to load into this homeserver. - config_files: - # - /path/to/appservice_registration.yaml - -# Configuration for the Client API. -client_api: - # Prevents new users from being able to register on this homeserver, except when - # using the registration shared secret below. - registration_disabled: false - - # Prevents new guest accounts from being created. Guest registration is also - # disabled implicitly by setting 'registration_disabled' above. - guests_disabled: true - - # If set, allows registration by anyone who knows the shared secret, regardless - # of whether registration is otherwise disabled. - registration_shared_secret: "{{REGISTRATION_SECRET}}" - - # Whether to require reCAPTCHA for registration. If you have enabled registration - # then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used - # for coordinated spam attacks. - enable_registration_captcha: false - - # Settings for ReCAPTCHA. - recaptcha_public_key: "" - recaptcha_private_key: "" - recaptcha_bypass_secret: "" - - # To use hcaptcha.com instead of ReCAPTCHA, set the following parameters, otherwise just keep them empty. - # recaptcha_siteverify_api: "https://hcaptcha.com/siteverify" - # recaptcha_api_js_url: "https://js.hcaptcha.com/1/api.js" - # recaptcha_form_field: "h-captcha-response" - # recaptcha_sitekey_class: "h-captcha" - - # TURN server information that this homeserver should send to clients. - turn: - turn_user_lifetime: "5m" - turn_uris: - # - turn:turn.server.org?transport=udp - # - turn:turn.server.org?transport=tcp - turn_shared_secret: "" - # If your TURN server requires static credentials, then you will need to enter - # them here instead of supplying a shared secret. Note that these credentials - # will be visible to clients! - # turn_username: "" - # turn_password: "" - - # Settings for rate-limited endpoints. Rate limiting kicks in after the threshold - # number of "slots" have been taken by requests from a specific host. Each "slot" - # will be released after the cooloff time in milliseconds. Server administrators - # and appservice users are exempt from rate limiting by default. - rate_limiting: - enabled: true - threshold: 20 - cooloff_ms: 500 - exempt_user_ids: - # - "@user:domain.com" - -# Configuration for the Federation API. -federation_api: - # How many times we will try to resend a failed transaction to a specific server. The - # backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc. Once - # the max retries are exceeded, Dendrite will no longer try to send transactions to - # that server until it comes back to life and connects to us again. - send_max_retries: 16 - - # Disable the validation of TLS certificates of remote federated homeservers. Do not - # enable this option in production as it presents a security risk! - disable_tls_validation: false - - # Disable HTTP keepalives, which also prevents connection reuse. Dendrite will typically - # keep HTTP connections open to remote hosts for 5 minutes as they can be reused much - # more quickly than opening new connections each time. Disabling keepalives will close - # HTTP connections immediately after a successful request but may result in more CPU and - # memory being used on TLS handshakes for each new connection instead. - disable_http_keepalives: false - - # Perspective keyservers to use as a backup when direct key fetches fail. This may - # be required to satisfy key requests for servers that are no longer online when - # joining some rooms. - key_perspectives: - - server_name: matrix.org - keys: - - key_id: ed25519:auto - public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw - - key_id: ed25519:a_RXGa - public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ - - # This option will control whether Dendrite will prefer to look up keys directly - # or whether it should try perspective servers first, using direct fetches as a - # last resort. - prefer_direct_fetch: false - - database: - connection_string: file:dendrite-federationapi.db - -# Configuration for the Media API. -media_api: - # Storage path for uploaded media. May be relative or absolute. - base_path: ./media_store - - # The maximum allowed file size (in bytes) for media uploads to this homeserver - # (0 = unlimited). If using a reverse proxy, ensure it allows requests at least - #this large (e.g. the client_max_body_size setting in nginx). - max_file_size_bytes: 10485760 - - # Whether to dynamically generate thumbnails if needed. - dynamic_thumbnails: false - - # The maximum number of simultaneous thumbnail generators to run. - max_thumbnail_generators: 10 - - # A list of thumbnail sizes to be generated for media content. - thumbnail_sizes: - - width: 32 - height: 32 - method: crop - - width: 96 - height: 96 - method: crop - - width: 640 - height: 480 - method: scale - - database: - connection_string: file:dendrite-mediaapi.db - -# Configuration for enabling experimental MSCs on this homeserver. -mscs: - mscs: - # - msc2836 # (Threading, see https://github.com/matrix-org/matrix-doc/pull/2836) - # - msc2946 # (Spaces Summary, see https://github.com/matrix-org/matrix-doc/pull/2946) - - database: - connection_string: file:dendrite-msc.db - -# Configuration for the Sync API. -sync_api: - # This option controls which HTTP header to inspect to find the real remote IP - # address of the client. This is likely required if Dendrite is running behind - # a reverse proxy server. - # real_ip_header: X-Real-IP - - # Configuration for the full-text search engine. - search: - # Whether or not search is enabled. - enabled: false - - # The path where the search index will be created in. - index_path: "./searchindex" - - # The language most likely to be used on the server - used when indexing, to - # ensure the returned results match expectations. A full list of possible languages - # can be found at https://github.com/blevesearch/bleve/tree/master/analysis/lang - language: "en" - - database: - connection_string: file:dendrite-syncapi.db - -# Configuration for the User API. -user_api: - # The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31 - # See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information. - # Setting this lower makes registration/login consume less CPU resources at the cost - # of security should the database be compromised. Setting this higher makes registration/login - # consume more CPU resources but makes it harder to brute force password hashes. This value - # can be lowered if performing tests or on embedded Dendrite instances (e.g WASM builds). - bcrypt_cost: 10 - - # The length of time that a token issued for a relying party from - # /_matrix/client/r0/user/{userId}/openid/request_token endpoint - # is considered to be valid in milliseconds. - # The default lifetime is 3600000ms (60 minutes). - # openid_token_lifetime_ms: 3600000 - - # Users who register on this homeserver will automatically be joined to the rooms listed under "auto_join_rooms" option. - # By default, any room aliases included in this list will be created as a publicly joinable room - # when the first user registers for the homeserver. If the room already exists, - # make certain it is a publicly joinable room, i.e. the join rule of the room must be set to 'public'. - # As Spaces are just rooms under the hood, Space aliases may also be used. - auto_join_rooms: - # - "#main:matrix.org" - - account_database: - connection_string: file:dendrite-userapi.db - -room_server: - database: - connection_string: file:dendrite-roomserverapi.db - -key_server: - database: - connection_string: file:dendrite-keyserverapi.db - -relay_api: - database: - connection_string: file:dendrite-relayapi.db - -# Configuration for Opentracing. -# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on -# how this works and how to set it up. -tracing: - enabled: false - jaeger: - serviceName: "" - disabled: false - rpc_metrics: false - tags: [] - sampler: null - reporter: null - headers: null - baggage_restrictions: null - throttler: null - -# Logging configuration. The "std" logging type controls the logs being sent to -# stdout. The "file" logging type controls logs being written to a log folder on -# the disk. Supported log levels are "debug", "info", "warn", "error". -logging: - - type: std - level: debug - - type: file - level: debug - params: - path: ./logs diff --git a/cypress/plugins/docker/index.ts b/cypress/plugins/docker/index.ts deleted file mode 100644 index e05b4a3c07..0000000000 --- a/cypress/plugins/docker/index.ts +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import * as os from "os"; -import * as crypto from "crypto"; -import * as childProcess from "child_process"; -import * as fse from "fs-extra"; - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; - -// A cypress plugin to run docker commands - -export async function dockerRun(opts: { - image: string; - containerName: string; - params?: string[]; - cmd?: string[]; -}): Promise { - const userInfo = os.userInfo(); - const params = opts.params ?? []; - - if (params?.includes("-v") && userInfo.uid >= 0) { - // Run the docker container as our uid:gid to prevent problems with permissions. - if (await isPodman()) { - // Note: this setup is for podman rootless containers. - - // In podman, run as root in the container, which maps to the current - // user on the host. This is probably the default since Synapse's - // Dockerfile doesn't specify, but we're being explicit here - // because it's important for the permissions to work. - params.push("-u", "0:0"); - - // Tell Synapse not to switch UID - params.push("-e", "UID=0"); - params.push("-e", "GID=0"); - } else { - params.push("-u", `${userInfo.uid}:${userInfo.gid}`); - } - } - - const args = [ - "run", - "--name", - `${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`, - "-d", - "--rm", - ...params, - opts.image, - ]; - - if (opts.cmd) args.push(...opts.cmd); - - return new Promise((resolve, reject) => { - childProcess.execFile("docker", args, (err, stdout) => { - if (err) reject(err); - resolve(stdout.trim()); - }); - }); -} - -export function dockerExec(args: { containerId: string; params: string[] }): Promise { - return new Promise((resolve, reject) => { - childProcess.execFile( - "docker", - ["exec", args.containerId, ...args.params], - { encoding: "utf8" }, - (err, stdout, stderr) => { - if (err) { - console.log(stdout); - console.log(stderr); - reject(err); - return; - } - resolve(); - }, - ); - }); -} - -export async function dockerLogs(args: { - containerId: string; - stdoutFile?: string; - stderrFile?: string; -}): Promise { - const stdoutFile = args.stdoutFile ? await fse.open(args.stdoutFile, "w") : "ignore"; - const stderrFile = args.stderrFile ? await fse.open(args.stderrFile, "w") : "ignore"; - - await new Promise((resolve) => { - childProcess - .spawn("docker", ["logs", args.containerId], { - stdio: ["ignore", stdoutFile, stderrFile], - }) - .once("close", resolve); - }); - - if (args.stdoutFile) await fse.close(stdoutFile); - if (args.stderrFile) await fse.close(stderrFile); -} - -export function dockerStop(args: { containerId: string }): Promise { - return new Promise((resolve, reject) => { - childProcess.execFile("docker", ["stop", args.containerId], (err) => { - if (err) reject(err); - resolve(); - }); - }); -} - -export function dockerRm(args: { containerId: string }): Promise { - return new Promise((resolve, reject) => { - childProcess.execFile("docker", ["rm", args.containerId], (err) => { - if (err) reject(err); - resolve(); - }); - }); -} - -export function dockerIp(args: { containerId: string }): Promise { - return new Promise((resolve, reject) => { - childProcess.execFile( - "docker", - ["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", args.containerId], - (err, stdout) => { - if (err) reject(err); - else resolve(stdout.trim()); - }, - ); - }); -} - -/** - * Detects whether the docker command is actually podman. - * To do this, it looks for "podman" in the output of "docker --help". - */ -export function isPodman(): Promise { - return new Promise((resolve, reject) => { - childProcess.execFile("docker", ["--help"], (err, stdout) => { - if (err) reject(err); - else resolve(stdout.toLowerCase().includes("podman")); - }); - }); -} - -/** - * Supply the right hostname to use to talk to the host machine. On Docker this - * is "host.docker.internal" and on Podman this is "host.containers.internal". - */ -export async function hostContainerName() { - return (await isPodman()) ? "host.containers.internal" : "host.docker.internal"; -} - -/** - * @type {Cypress.PluginConfig} - */ -export function docker(on: PluginEvents, config: PluginConfigOptions) { - on("task", { - dockerRun, - dockerExec, - dockerLogs, - dockerStop, - dockerRm, - dockerIp, - }); -} diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts deleted file mode 100644 index d13f025caa..0000000000 --- a/cypress/plugins/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// -import installLogsPrinter from "cypress-terminal-report/src/installLogsPrinter"; -import { initPlugins } from "cypress-plugin-init"; - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; -import { synapseDocker } from "./synapsedocker"; -import { dendriteDocker } from "./dendritedocker"; -import { webserver } from "./webserver"; -import { docker } from "./docker"; -import { log } from "./log"; - -/** - * @type {Cypress.PluginConfig} - */ -export default function (on: PluginEvents, config: PluginConfigOptions) { - initPlugins(on, [docker, synapseDocker, dendriteDocker, webserver, log], config); - installLogsPrinter(on, { - printLogsToConsole: "never", - - // write logs to cypress/results/cypresslogs/.txt - outputRoot: "cypress/results", - outputTarget: { - "cypresslogs|txt": "txt", - }, - - // strip 'cypress/e2e' from log filenames - specRoot: "cypress/e2e", - }); -} diff --git a/cypress/plugins/log.ts b/cypress/plugins/log.ts deleted file mode 100644 index 4b16c9b8cd..0000000000 --- a/cypress/plugins/log.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; - -export function log(on: PluginEvents, config: PluginConfigOptions) { - on("task", { - log(message: string) { - console.log(message); - - return null; - }, - table(message: string) { - console.table(message); - - return null; - }, - }); -} diff --git a/cypress/plugins/mailhog/index.ts b/cypress/plugins/mailhog/index.ts deleted file mode 100644 index a156e93981..0000000000 --- a/cypress/plugins/mailhog/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; -import { getFreePort } from "../utils/port"; -import { dockerIp, dockerRun, dockerStop } from "../docker"; - -// A cypress plugins to add command to manage an instance of Mailhog in Docker - -export interface Instance { - host: string; - smtpPort: number; - httpPort: number; - containerId: string; -} - -const instances = new Map(); - -// Start a synapse instance: the template must be the name of -// one of the templates in the cypress/plugins/synapsedocker/templates -// directory -async function mailhogStart(): Promise { - const smtpPort = await getFreePort(); - const httpPort = await getFreePort(); - - console.log(`Starting mailhog...`); - - const containerId = await dockerRun({ - image: "mailhog/mailhog:latest", - containerName: `react-sdk-cypress-mailhog`, - params: ["--rm", "-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`], - }); - - console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`); - - const host = await dockerIp({ containerId }); - const instance: Instance = { smtpPort, httpPort, containerId, host }; - instances.set(containerId, instance); - return instance; -} - -async function mailhogStop(id: string): Promise { - const synCfg = instances.get(id); - - if (!synCfg) throw new Error("Unknown mailhog ID"); - - await dockerStop({ - containerId: id, - }); - - instances.delete(id); - - console.log(`Stopped mailhog id ${id}.`); - // cypress deliberately fails if you return 'undefined', so - // return null to signal all is well, and we've handled the task. - return null; -} - -/** - * @type {Cypress.PluginConfig} - */ -export function mailhogDocker(on: PluginEvents, config: PluginConfigOptions) { - on("task", { - mailhogStart, - mailhogStop, - }); - - on("after:spec", async (spec) => { - // Cleans up any remaining instances after a spec run - for (const synId of instances.keys()) { - console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`); - await mailhogStop(synId); - } - }); -} diff --git a/cypress/plugins/synapsedocker/index.ts b/cypress/plugins/synapsedocker/index.ts deleted file mode 100644 index 7c278610cc..0000000000 --- a/cypress/plugins/synapsedocker/index.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import * as path from "path"; -import * as os from "os"; -import * as crypto from "crypto"; -import * as fse from "fs-extra"; - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; -import { getFreePort } from "../utils/port"; -import { dockerExec, dockerLogs, dockerRun, dockerStop, hostContainerName, isPodman } from "../docker"; -import { HomeserverConfig, HomeserverInstance } from "../utils/homeserver"; -import { StartHomeserverOpts } from "../../support/homeserver"; - -// A cypress plugins to add command to start & stop synapses in -// docker with preset templates. - -const synapses = new Map(); - -function randB64Bytes(numBytes: number): string { - return crypto.randomBytes(numBytes).toString("base64").replace(/=*$/, ""); -} - -async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise { - const templateDir = path.join(__dirname, "templates", opts.template); - - const stats = await fse.stat(templateDir); - if (!stats?.isDirectory) { - throw new Error(`No such template: ${opts.template}`); - } - const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-synapsedocker-")); - - // copy the contents of the template dir, omitting homeserver.yaml as we'll template that - console.log(`Copy ${templateDir} -> ${tempDir}`); - await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== "homeserver.yaml" }); - - const registrationSecret = randB64Bytes(16); - const macaroonSecret = randB64Bytes(16); - const formSecret = randB64Bytes(16); - - const port = await getFreePort(); - const baseUrl = `http://localhost:${port}`; - - // now copy homeserver.yaml, applying substitutions - const templateHomeserver = path.join(templateDir, "homeserver.yaml"); - const outputHomeserver = path.join(tempDir, "homeserver.yaml"); - console.log(`Gen ${templateHomeserver} -> ${outputHomeserver}`); - let hsYaml = await fse.readFile(templateHomeserver, "utf8"); - hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); - hsYaml = hsYaml.replace(/{{MACAROON_SECRET_KEY}}/g, macaroonSecret); - hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret); - hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl); - hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort?.toString()); - hsYaml = hsYaml.replace(/{{HOST_DOCKER_INTERNAL}}/g, await hostContainerName()); - if (opts.variables) { - let fetchedHostContainer = null; - for (const key in opts.variables) { - let value = String(opts.variables[key]); - - if (value === "{{HOST_DOCKER_INTERNAL}}") { - if (!fetchedHostContainer) { - fetchedHostContainer = await hostContainerName(); - } - value = fetchedHostContainer; - } - - hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), value); - } - } - - await fse.writeFile(outputHomeserver, hsYaml); - - // now generate a signing key (we could use synapse's config generation for - // this, or we could just do this...) - // NB. This assumes the homeserver.yaml specifies the key in this location - const signingKey = randB64Bytes(32); - const outputSigningKey = path.join(tempDir, "localhost.signing.key"); - console.log(`Gen -> ${outputSigningKey}`); - await fse.writeFile(outputSigningKey, `ed25519 x ${signingKey}`); - - return { - port, - baseUrl, - configDir: tempDir, - registrationSecret, - }; -} - -/** - * Start a synapse instance: the template must be the name of - * one of the templates in the cypress/plugins/synapsedocker/templates - * directory. - * - * Any value in opts.variables that is set to `{{HOST_DOCKER_INTERNAL}}' - * will be replaced with 'host.docker.internal' (if we are on Docker) or - * 'host.containers.interal' if we are on Podman. - */ -async function synapseStart(opts: StartHomeserverOpts): Promise { - const synCfg = await cfgDirFromTemplate(opts); - - console.log(`Starting synapse with config dir ${synCfg.configDir}...`); - - const dockerSynapseParams = ["--rm", "-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`]; - - if (await isPodman()) { - // Make host.containers.internal work to allow Synapse to talk to the - // test OIDC server. - dockerSynapseParams.push("--network"); - dockerSynapseParams.push("slirp4netns:allow_host_loopback=true"); - } else { - // Make host.docker.internal work to allow Synapse to talk to the test - // OIDC server. - dockerSynapseParams.push("--add-host"); - dockerSynapseParams.push("host.docker.internal:host-gateway"); - } - - const synapseId = await dockerRun({ - image: "matrixdotorg/synapse:develop", - containerName: `react-sdk-cypress-synapse`, - params: dockerSynapseParams, - cmd: ["run"], - }); - - console.log(`Started synapse with id ${synapseId} on port ${synCfg.port}.`); - - // Await Synapse healthcheck - await dockerExec({ - containerId: synapseId, - params: [ - "curl", - "--connect-timeout", - "30", - "--retry", - "30", - "--retry-delay", - "1", - "--retry-all-errors", - "--silent", - "http://localhost:8008/health", - ], - }); - - const synapse: HomeserverInstance = { serverId: synapseId, ...synCfg }; - synapses.set(synapseId, synapse); - return synapse; -} - -async function synapseStop(id: string): Promise { - const synCfg = synapses.get(id); - - if (!synCfg) throw new Error("Unknown synapse ID"); - - const synapseLogsPath = path.join("cypress", "synapselogs", id); - await fse.ensureDir(synapseLogsPath); - - await dockerLogs({ - containerId: id, - stdoutFile: path.join(synapseLogsPath, "stdout.log"), - stderrFile: path.join(synapseLogsPath, "stderr.log"), - }); - - await dockerStop({ - containerId: id, - }); - - await fse.remove(synCfg.configDir); - - synapses.delete(id); - - console.log(`Stopped synapse id ${id}.`); - // cypress deliberately fails if you return 'undefined', so - // return null to signal all is well, and we've handled the task. - return null; -} - -/** - * @type {Cypress.PluginConfig} - */ -export function synapseDocker(on: PluginEvents, config: PluginConfigOptions) { - on("task", { - synapseStart, - synapseStop, - }); - - on("after:spec", async (spec) => { - // Cleans up any remaining synapse instances after a spec run - // This is on the theory that we should avoid re-using synapse - // instances between spec runs: they should be cheap enough to - // start that we can have a separate one for each spec run or even - // test. If we accidentally re-use synapses, we could inadvertently - // make our tests depend on each other. - for (const synId of synapses.keys()) { - console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`); - await synapseStop(synId); - } - }); - - on("before:run", async () => { - // tidy up old synapse log files before each run - await fse.emptyDir(path.join("cypress", "synapselogs")); - }); -} diff --git a/cypress/plugins/synapsedocker/templates/COPYME/README.md b/cypress/plugins/synapsedocker/templates/COPYME/README.md deleted file mode 100644 index df1ed89e6e..0000000000 --- a/cypress/plugins/synapsedocker/templates/COPYME/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Meta-template for synapse templates - -To make another template, you can copy this directory diff --git a/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml b/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml deleted file mode 100644 index cb58dc8661..0000000000 --- a/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml +++ /dev/null @@ -1,72 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -# XXX: This won't actually be right: it lets docker allocate an ephemeral port, -# so we have a chicken-and-egg problem -public_baseurl: http://localhost:8008/ -# Listener is always port 8008 (configured in the container) -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client, federation, consent] - compress: false - -# An sqlite in-memory database is fast & automatically wipes each time -database: - name: "sqlite3" - args: - database: ":memory:" - -# Needs to be configured to log to the console like a good docker process -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -# These placeholders will be be replaced with values generated at start -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -# Signing key must be here: it will be generated to this file -signing_key_path: "/data/localhost.signing.key" -email: - enable_notifs: false - smtp_host: "localhost" - smtp_port: 25 - smtp_user: "exampleusername" - smtp_pass: "examplepassword" - require_transport_security: False - notif_from: "Your Friendly %(app)s homeserver " - app_name: Matrix - notif_template_html: notif_mail.html - notif_template_text: notif_mail.txt - notif_for_new_users: True - client_base_url: "http://localhost/element" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true diff --git a/cypress/plugins/synapsedocker/templates/COPYME/log.config b/cypress/plugins/synapsedocker/templates/COPYME/log.config deleted file mode 100644 index ac232762da..0000000000 --- a/cypress/plugins/synapsedocker/templates/COPYME/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: INFO - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/cypress/plugins/synapsedocker/templates/consent/README.md b/cypress/plugins/synapsedocker/templates/consent/README.md deleted file mode 100644 index 713e55f9d5..0000000000 --- a/cypress/plugins/synapsedocker/templates/consent/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with user privacy consent enabled diff --git a/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml b/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml deleted file mode 100644 index d3a4fa520c..0000000000 --- a/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml +++ /dev/null @@ -1,84 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client, federation, consent] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" -email: - enable_notifs: false - smtp_host: "localhost" - smtp_port: 25 - smtp_user: "exampleusername" - smtp_pass: "examplepassword" - require_transport_security: False - notif_from: "Your Friendly %(app)s homeserver " - app_name: Matrix - notif_template_html: notif_mail.html - notif_template_text: notif_mail.txt - notif_for_new_users: True - client_base_url: "http://localhost/element" - -user_consent: - template_dir: /data/res/templates/privacy - version: 1.0 - server_notice_content: - msgtype: m.text - body: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - send_server_notice_to_guests: True - block_events_error: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - require_at_registration: true - -server_notices: - system_mxid_localpart: notices - system_mxid_display_name: "Server Notices" - system_mxid_avatar_url: "mxc://localhost:5005/oumMVlgDnLYFaPVkExemNVVZ" - room_name: "Server Notices" -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true diff --git a/cypress/plugins/synapsedocker/templates/consent/log.config b/cypress/plugins/synapsedocker/templates/consent/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/cypress/plugins/synapsedocker/templates/consent/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html deleted file mode 100644 index bcc7a590bb..0000000000 --- a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - Test Privacy policy - - - {% if has_consented %} -

Thank you, you've already accepted the license.

- {% else %} -

Please accept the license!

-
- - - - -
- {% endif %} - - diff --git a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html deleted file mode 100644 index 2a2b21eef4..0000000000 --- a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Test Privacy policy - - -

Danke schoen

- - diff --git a/cypress/plugins/synapsedocker/templates/default/README.md b/cypress/plugins/synapsedocker/templates/default/README.md deleted file mode 100644 index 8f6b11f999..0000000000 --- a/cypress/plugins/synapsedocker/templates/default/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with user privacy consent disabled diff --git a/cypress/plugins/synapsedocker/templates/default/homeserver.yaml b/cypress/plugins/synapsedocker/templates/default/homeserver.yaml deleted file mode 100644 index e51ac1918f..0000000000 --- a/cypress/plugins/synapsedocker/templates/default/homeserver.yaml +++ /dev/null @@ -1,94 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 -rc_joins: - local: - per_second: 9999 - burst_count: 9999 - remote: - per_second: 9999 - burst_count: 9999 -rc_joins_per_room: - per_second: 9999 - burst_count: 9999 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 - -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -oidc_providers: - - idp_id: test - idp_name: "OAuth test" - issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth" - authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html" - # the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container. - # Hence, HOST_DOCKER_INTERNAL rather than localhost. This is set to - # host.docker.internal on Docker and host.containers.internal on Podman. - token_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/token" - userinfo_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/userinfo" - client_id: "synapse" - discover: false - scopes: ["profile"] - skip_verification: true - user_mapping_provider: - config: - display_name_template: "{{ user.name }}" diff --git a/cypress/plugins/synapsedocker/templates/default/log.config b/cypress/plugins/synapsedocker/templates/default/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/cypress/plugins/synapsedocker/templates/default/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/cypress/plugins/synapsedocker/templates/email/README.md b/cypress/plugins/synapsedocker/templates/email/README.md deleted file mode 100644 index 40c23ba0be..0000000000 --- a/cypress/plugins/synapsedocker/templates/email/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured to require an email for registration diff --git a/cypress/plugins/synapsedocker/templates/email/homeserver.yaml b/cypress/plugins/synapsedocker/templates/email/homeserver.yaml deleted file mode 100644 index fc20641ab4..0000000000 --- a/cypress/plugins/synapsedocker/templates/email/homeserver.yaml +++ /dev/null @@ -1,44 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -registrations_require_3pid: - - email -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -email: - smtp_host: "%SMTP_HOST%" - smtp_port: %SMTP_PORT% - notif_from: "Your Friendly %(app)s homeserver " - app_name: my_branded_matrix_server diff --git a/cypress/plugins/synapsedocker/templates/email/log.config b/cypress/plugins/synapsedocker/templates/email/log.config deleted file mode 100644 index ac232762da..0000000000 --- a/cypress/plugins/synapsedocker/templates/email/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: INFO - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/cypress/plugins/utils/homeserver.ts b/cypress/plugins/utils/homeserver.ts deleted file mode 100644 index d6a4de0411..0000000000 --- a/cypress/plugins/utils/homeserver.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2023 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -export interface HomeserverConfig { - configDir: string; - registrationSecret: string; - baseUrl: string; - port: number; -} - -export interface HomeserverInstance extends HomeserverConfig { - serverId: string; -} diff --git a/cypress/plugins/utils/port.ts b/cypress/plugins/utils/port.ts deleted file mode 100644 index 156ba866d5..0000000000 --- a/cypress/plugins/utils/port.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as net from "net"; - -export async function getFreePort(): Promise { - return new Promise((resolve) => { - const srv = net.createServer(); - srv.listen(0, () => { - const port = (srv.address()).port; - srv.close(() => resolve(port)); - }); - }); -} diff --git a/cypress/plugins/webserver.ts b/cypress/plugins/webserver.ts deleted file mode 100644 index 55a25a313e..0000000000 --- a/cypress/plugins/webserver.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import * as http from "http"; -import { AddressInfo } from "net"; - -import PluginEvents = Cypress.PluginEvents; -import PluginConfigOptions = Cypress.PluginConfigOptions; - -const servers: http.Server[] = []; - -function serveHtmlFile(html: string): string { - const server = http.createServer((req, res) => { - res.writeHead(200, { - "Content-Type": "text/html", - }); - res.end(html); - }); - server.listen(); - servers.push(server); - - return `http://localhost:${(server.address() as AddressInfo).port}/`; -} - -function stopWebServers(): null { - for (const server of servers) { - server.close(); - } - servers.splice(0, servers.length); // clear - - return null; // tell cypress we did the task successfully (doesn't allow undefined) -} - -export function webserver(on: PluginEvents, config: PluginConfigOptions) { - on("task", { serveHtmlFile, stopWebServers }); - on("after:run", stopWebServers); -} diff --git a/cypress/support/app.ts b/cypress/support/app.ts deleted file mode 100644 index 3e9d75173a..0000000000 --- a/cypress/support/app.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import Chainable = Cypress.Chainable; -import AUTWindow = Cypress.AUTWindow; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - interface Chainable { - /** - * Applies tweaks to the config read from config.json - */ - tweakConfig(tweaks: Record): Chainable; - } - } -} - -Cypress.Commands.add("tweakConfig", (tweaks: Record): Chainable => { - return cy.window().then((win) => { - // note: we can't *set* the object because the window version is effectively a pointer. - for (const [k, v] of Object.entries(tweaks)) { - // @ts-ignore - for some reason it's not picking up on global.d.ts types. - win.mxReactSdkConfig[k] = v; - } - }); -}); - -// Needed to make this file a module -export {}; diff --git a/cypress/support/axe.ts b/cypress/support/axe.ts deleted file mode 100644 index b830542902..0000000000 --- a/cypress/support/axe.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// - -import "cypress-axe"; -import * as axe from "axe-core"; - -import type { Options } from "cypress-axe"; -import Chainable = Cypress.Chainable; - -function terminalLog(violations: axe.Result[]): void { - cy.task( - "log", - `${violations.length} accessibility violation${violations.length === 1 ? "" : "s"} ${ - violations.length === 1 ? "was" : "were" - } detected`, - ); - - // pluck specific keys to keep the table readable - const violationData = violations.map(({ id, impact, description, nodes }) => ({ - id, - impact, - description, - nodes: nodes.length, - })); - - cy.task("table", violationData); -} - -Cypress.Commands.overwrite( - "checkA11y", - ( - originalFn: Chainable["checkA11y"], - context?: string | Node | axe.ContextObject | undefined, - options: Options = {}, - violationCallback?: ((violations: axe.Result[]) => void) | undefined, - skipFailures?: boolean, - ): void => { - return originalFn( - context, - { - ...options, - rules: { - // Disable contrast checking for now as we have too many issues with it - "color-contrast": { - enabled: false, - }, - ...options.rules, - }, - }, - violationCallback ?? terminalLog, - skipFailures, - ); - }, -); - -// Load axe-core into the window under test. -// -// The injectAxe in cypress-axe attempts to load axe via an `eval`. That conflicts with our CSP -// which disallows "unsafe-eval". So, replace it with an implementation that loads it via an -// injected