Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
1f98b5f
test: add e2e tests
BreadGenie Jan 27, 2026
13f7cb3
ci: add cache and use diff approach for starting frappe server
BreadGenie Jan 27, 2026
e966783
ci: add test site to hosts
BreadGenie Jan 27, 2026
7c1823a
ci: upload whole test results on failure
BreadGenie Jan 27, 2026
46e5822
test: remove custom hardcoded timeouts
BreadGenie Jan 27, 2026
7f7f92a
test: don't wait for preview page if you're creating a meeting
BreadGenie Jan 27, 2026
a1b55ec
refactor: remove unneeded commit
BreadGenie Jan 27, 2026
f902555
ci: remove duplicate upload
BreadGenie Jan 27, 2026
b0de9c0
fix: temp run 1 test
BreadGenie Jan 27, 2026
f1d2ef5
ci: use `--unsafely-treat-insecure-origin-as-secure` flag
BreadGenie Jan 28, 2026
677d675
ci: temp don't retry
BreadGenie Jan 28, 2026
c510e27
ci: use new headless
BreadGenie Jan 28, 2026
0b11fe4
ci: use dummy video as well
BreadGenie Jan 28, 2026
7ba9512
test: try different way to locate button
BreadGenie Jan 29, 2026
8b1cb87
ci: don't use dummy data as it likes to break getDisplayMedia
BreadGenie Jan 29, 2026
ebd0de2
test: temp try patching getUserMedia
BreadGenie Jan 29, 2026
1f28508
test: remove wait for function
BreadGenie Jan 29, 2026
78c6d3c
test: move mouse right before clicking buttons
BreadGenie Jan 29, 2026
5b6f9ed
test: refactor reveal toolbar fn
BreadGenie Jan 29, 2026
2d73aac
fix: don't fetch existing waiting user if user not a host
BreadGenie Jan 29, 2026
6ab97dc
test: move mouse before checking button state
BreadGenie Jan 29, 2026
7bb2db6
test: wait for container to be visible
BreadGenie Jan 29, 2026
2a59246
test: increase timeout and debug code
BreadGenie Jan 29, 2026
f3d3bf4
ci: bump up playwright to latest
BreadGenie Jan 29, 2026
c3ff395
test: don't wait for container
BreadGenie Jan 29, 2026
f75d567
ci: use only 1 worker for sfu
BreadGenie Jan 29, 2026
0bb01ae
test: retry twice in CI
BreadGenie Jan 29, 2026
c9bd8ee
ci: run all e2e tests
BreadGenie Jan 29, 2026
d69bc18
ci: failfast
BreadGenie Jan 29, 2026
52ce89f
test: use login via api to speed up test failure
BreadGenie Jan 30, 2026
9a7de06
ci: tune for ci
BreadGenie Jan 30, 2026
0d55f17
ci: use macos
BreadGenie Jan 30, 2026
a37bdd1
test: increase timeout for ci
BreadGenie Jan 30, 2026
936e016
ci: use macos with apple silicon
BreadGenie Feb 2, 2026
a68525b
refactor: use yarn instead of npm
BreadGenie Feb 2, 2026
9a6ff34
uncommit this please
BreadGenie Feb 3, 2026
5d0458e
ci: use external sfu
BreadGenie Feb 10, 2026
8cb99c8
ci: option to configure key
BreadGenie Feb 10, 2026
9be9167
Merge branch 'develop' into e2e-tests
BreadGenie Apr 6, 2026
217da57
Revert "ci: use macos"
BreadGenie Apr 6, 2026
edc59b6
Revert "ci: option to configure key"
BreadGenie Apr 6, 2026
e5035de
Revert "ci: use external sfu"
BreadGenie Apr 6, 2026
39b2dd7
Revert "uncommit this please"
BreadGenie Apr 6, 2026
7a07b94
Revert "ci: use macos with apple silicon"
BreadGenie Apr 6, 2026
eaec8a4
Revert "test: increase timeout for ci"
BreadGenie Apr 6, 2026
a95b2c4
chore: remove merge conflict markers
BreadGenie Apr 6, 2026
d7eb8cd
chore: merge develop into e2e-tests, resolve ScreenShareLayout.vue co…
Copilot Apr 14, 2026
c0670ab
refactor(layout): unify layout logic (#37)
BreadGenie Apr 13, 2026
2d316bf
test: add e2e tests
BreadGenie Jan 27, 2026
c66234a
uncommit this please
BreadGenie Feb 3, 2026
507fabd
Revert "uncommit this please"
BreadGenie Apr 6, 2026
ae77cdd
chore: remove merge conflict markers
BreadGenie Apr 6, 2026
7d9d27d
chore: remove conflict markers
BreadGenie Apr 14, 2026
a7b2a1b
refactor: rewrite e2e tests
BreadGenie Apr 14, 2026
741d8ae
ci: fix yaml file
BreadGenie Apr 14, 2026
85e9261
test: create meeting via ui
BreadGenie Apr 14, 2026
2883841
test: run with just one worker
BreadGenie Apr 14, 2026
3a2a47d
test: add proper waits
BreadGenie Apr 14, 2026
37ee1a6
test: better join as guest
BreadGenie Apr 14, 2026
1f7dce6
ci: enable manual run option
BreadGenie Apr 14, 2026
4eda90b
ci: optimise test artifact overhead
BreadGenie Apr 14, 2026
d72bbcb
ci: increase timeout for one test
BreadGenie Apr 14, 2026
e2919d3
test: api first auth and meeting creation
BreadGenie Apr 14, 2026
2f321a3
test: mark as not parallel and optimise timeouts
BreadGenie Apr 14, 2026
89b0cec
test: refactor create api call
BreadGenie Apr 14, 2026
a52e0fb
ci: ignore csrf
BreadGenie Apr 14, 2026
845f9ff
test: skip multi user test temporarily
BreadGenie Apr 14, 2026
e977eeb
test: add restricted meeting and host control tests
BreadGenie Apr 14, 2026
817d049
fix: don't rate limit if in CI
BreadGenie Apr 14, 2026
fb55743
refactor: better ci or dev detection
BreadGenie Apr 14, 2026
637bde2
refactor: pre-create meetings wherever it makes sense
BreadGenie Apr 14, 2026
168f27f
refactor: attempt to avoid timeouts
BreadGenie Apr 14, 2026
5092e12
refactor: increase timeouts and catching joining meeting error
BreadGenie Apr 14, 2026
158b6a5
test: clear rate limiting key before every test
BreadGenie Apr 15, 2026
6b01682
test: clear rate limiting key for the right method
BreadGenie Apr 15, 2026
7adc80b
test: failfast with max 3 failures
BreadGenie Apr 15, 2026
12845db
test: unskip the test
BreadGenie Apr 15, 2026
1f73a0f
refactor: avoid duplicate api calls for join as guest
BreadGenie Apr 15, 2026
126064c
test: properly clear rate limit value
BreadGenie Apr 15, 2026
ed9b1db
chore: ignore semgrep and remove unused code
BreadGenie Apr 15, 2026
c2ef3d7
test: ensure we check peerConnection status
BreadGenie Apr 15, 2026
a315c83
ci: run test on 2 workers
BreadGenie Apr 15, 2026
7b3a2f6
Revert "ci: run test on 2 workers"
BreadGenie Apr 15, 2026
65492ea
test: disable audio processing and reduce video fps
BreadGenie Apr 15, 2026
d320392
test: chat, peer removal and screenshare exit
BreadGenie Apr 15, 2026
bc00c60
Revert "test: ensure we check peerConnection status"
BreadGenie Apr 15, 2026
385a5ad
refactor: remove classes and bits of code that's not needed
BreadGenie Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
name: E2E Tests

on:
push:
branches:
- develop
- main
pull_request:
workflow_dispatch:

concurrency:
group: e2e-${{ github.event.number || github.sha }}
cancel-in-progress: true

jobs:
e2e:
runs-on: ubuntu-latest
name: E2E Tests

services:
redis-cache:
image: redis:alpine
ports:
- 13000:6379
redis-queue:
image: redis:alpine
ports:
- 11000:6379
mariadb:
image: mariadb:10.6
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3

steps:
- name: Clone
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.14"

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
check-latest: true

- name: Add to Hosts
run: echo "127.0.0.1 meet.test" | sudo tee -a /etc/hosts

- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT'

- name: Cache yarn
uses: actions/cache@v4
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('e2e/package.json') }}
restore-keys: |
${{ runner.os }}-playwright-

- name: Install MariaDB Client
run: |
sudo apt update
sudo apt-get install -y mariadb-client

- name: Setup Bench
run: |
pip install frappe-bench
bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"

- name: Install Meet
working-directory: /home/runner/frappe-bench
run: |
bench get-app meet $GITHUB_WORKSPACE
bench setup requirements --dev
bench new-site --db-root-password root --admin-password admin meet.test
bench --site meet.test install-app meet
bench build
env:
CI: "Yes"

- name: Configure Site for E2E
working-directory: /home/runner/frappe-bench
run: |
bench --site meet.test set-config sfu_secret "e2e-test-secret-key-12345"
bench --site meet.test set-config sfu_server_url "http://localhost"
bench --site meet.test set-config sfu_server_port "3000"
bench --site meet.test set-config ignore_csrf 1
bench --site meet.test set-config allow_tests true
bench --site meet.test set-config host_name "http://meet.test:8000"

- name: Build SFU Server
working-directory: ${{ github.workspace }}/sfu-server
run: |
yarn install --frozen-lockfile
yarn build

- name: Install E2E Dependencies
working-directory: ${{ github.workspace }}/e2e
run: |
yarn install --non-interactive
yarn playwright install chromium --with-deps

- name: Start Frappe Server
working-directory: /home/runner/frappe-bench
run: |
sed -i 's/^watch:/# watch:/g' Procfile
sed -i 's/^schedule:/# schedule:/g' Procfile
bench start &> bench_start.log &
echo "Waiting for Frappe server to start..."
timeout 60 bash -c 'until curl -s http://meet.test:8000 > /dev/null; do sleep 2; done'
echo "Frappe server is ready!"

- name: Start SFU Server
working-directory: ${{ github.workspace }}/sfu-server
run: |
yarn start &
echo "Waiting for SFU server..."
timeout 30 bash -c 'until curl -s http://localhost:3000/health > /dev/null 2>&1; do sleep 2; done'
echo "SFU server is ready"
env:
PORT: 3000
HOST: 0.0.0.0
JWT_SECRET: e2e-test-secret-key-12345
MEDIASOUP_ANNOUNCED_IP: 127.0.0.1
MEDIASOUP_NUM_WORKERS: 1

- name: Run E2E Tests
working-directory: ${{ github.workspace }}/e2e
run: yarn test
env:
BASE_URL: http://meet.test:8000
SFU_URL: http://localhost:3000
CI: true

- name: Upload Test Results
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: |
e2e/playwright-report/
e2e/test-results/
compression-level: 1
retention-days: 7
6 changes: 6 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
playwright-report/
test-results/
results.xml
.state/
.DS_Store
61 changes: 61 additions & 0 deletions e2e/fixtures/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export const STUB_MEDIA_SCRIPT = `(() => {
window.localStorage.setItem("mediaPref.autoHideToolbar", "0");

if (!navigator.mediaDevices) {
Object.defineProperty(navigator, "mediaDevices", {
value: {},
configurable: true,
});
}

function createFakeStream(label) {
const canvas = document.createElement("canvas");
canvas.width = 1280;
canvas.height = 720;
const context = canvas.getContext("2d");
let tick = 0;

const draw = () => {
if (!context) {
return;
}
context.fillStyle = "#111827";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#f9fafb";
context.font = "32px sans-serif";
context.fillText(label, 48, 80);
context.font = "20px monospace";
context.fillText(String(++tick), 48, 128);
requestAnimationFrame(draw);
};

draw();

const stream = canvas.captureStream(12);

try {
const AudioContextCtor = window.AudioContext || window.webkitAudioContext;
if (AudioContextCtor) {
const audioContext = new AudioContextCtor();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
const destination = audioContext.createMediaStreamDestination();
gainNode.gain.value = 0.001;
oscillator.connect(gainNode);
gainNode.connect(destination);
oscillator.start();
for (const track of destination.stream.getAudioTracks()) {
stream.addTrack(track);
}
}
} catch {}

return stream;
}

const userStream = createFakeStream("camera");
const displayStream = createFakeStream("screen");

navigator.mediaDevices.getUserMedia = async () => userStream.clone();
navigator.mediaDevices.getDisplayMedia = async () => displayStream.clone();
})();`;
Loading
Loading