Skip to content

Commit

Permalink
Merge pull request #576 from shiguredo/feature/add-e2e-test-signaling…
Browse files Browse the repository at this point in the history
…-message-type-close

type: close の E2E テストを追加する
  • Loading branch information
voluntas authored Dec 8, 2024
2 parents 390aaab + 1c243cd commit 98161dd
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 96 deletions.
4 changes: 3 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
VITE_SORA_SIGNALING_URL=ws://127.0.0.1:5000/signaling
# テストに利用する Sora の ChannelID のプレフィックスを指定してください
VITE_SORA_CHANNEL_ID_PREFIX=sora-js-sdk-e2e-test_
# テストに利用するアクセストークンを指定してください、不要であれば何の値でも問題ありません
# テストに利用する Sora の API URL を指定ください、不要であれば空欄で大丈夫です
VITE_SORA_API_URL=http://127.0.0.1:5000/
# テストに利用するアクセストークンを指定してください、不要であれば空欄で大丈夫です
VITE_ACCESS_TOKEN=access_token
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
node: ["18", "20", "22"]
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
jobs:
e2e-test:
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
node: ["18", "20", "22"]
Expand All @@ -25,9 +25,15 @@ jobs:
env:
VITE_SORA_SIGNALING_URL: ${{ secrets.TEST_SIGNALING_URL }}
VITE_SORA_CHANNEL_ID_PREFIX: ${{ secrets.TEST_CHANNEL_ID_PREFIX }}
VITE_SORA_API_URL: ${{ secrets.TEST_API_URL }}
VITE_ACCESS_TOKEN: ${{ secrets.TEST_SECRET_KEY }}
steps:
- uses: actions/checkout@v4
- uses: tailscale/github-action@v2
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
Expand All @@ -48,7 +54,7 @@ jobs:

slack_notify_succeeded:
needs: [e2e-test]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: success()
steps:
- name: Slack Notification
Expand All @@ -62,7 +68,7 @@ jobs:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
slack_notify_failed:
needs: [e2e-test]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: failure()
steps:
- name: Slack Notification
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/npm-pkg-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
jobs:
npm-pkg-e2e-test:
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
# メンテナンスはおてて
Expand All @@ -38,10 +38,16 @@ jobs:
env:
VITE_SORA_SIGNALING_URL: ${{ secrets.TEST_SIGNALING_URL }}
VITE_SORA_CHANNEL_ID_PREFIX: ${{ secrets.TEST_CHANNEL_ID_PREFIX }}
VITE_SORA_API_URL: ${{ secrets.TEST_API_URL }}
VITE_ACCESS_TOKEN: ${{ secrets.TEST_SECRET_KEY }}
NPM_PKG_E2E_TEST: true
steps:
- uses: actions/checkout@v4
- uses: tailscale/github-action@v2
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
Expand All @@ -66,7 +72,7 @@ jobs:

slack_notify_succeeded:
needs: [npm-pkg-e2e-test]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: success()
steps:
- name: Slack Notification
Expand All @@ -79,7 +85,7 @@ jobs:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
slack_notify_failed:
needs: [npm-pkg-e2e-test]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: failure()
steps:
- name: Slack Notification
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

### misc

- [ADD] E2E テストに `"type": "close"` のテストを追加する
- @voluntas
- [ADD] E2E テストに `"type": "switched"` のテストを追加する
- @voluntas
- [CHANGE] canary リリース方法を `canary.py` に変更する
Expand Down
26 changes: 26 additions & 0 deletions examples/data_channel_signaling_only/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<html lang="ja">

<head>
<meta charset="utf-8">
<title>Sendonly test</title>
</head>

<body>
<div class="container">
<h1>Sendonly test</h1>
<h3 id="sdk-version"></h3>
<button id="connect">connect</button>
<button id="disconnect">disconnect</button>
<button id="disconnect-api">disconnect-api</button>
<button id="get-stats">getStats</button><br />
<div id="connection-id"></div>
<video id="local-video" autoplay="" playsinline="" controls="" muted=""
style="width: 320px; height: 240px; border: 1px solid black;"></video>
<div id="stats-report" style="white-space: pre-wrap; font-family: monospace;"></div>
<div id="stats-report-json"></div>
</div>

<script type="module" src="./main.mts"></script>
</body>

</html>
167 changes: 167 additions & 0 deletions examples/data_channel_signaling_only/main.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import Sora, {
type SignalingNotifyMessage,
type SignalingEvent,
type ConnectionPublisher,
type SoraConnection,
type ConnectionOptions,
} from 'sora-js-sdk'

document.addEventListener('DOMContentLoaded', async () => {
const SORA_SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL
const SORA_CHANNEL_ID_PREFIX = import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || ''
const SORA_CHANNEL_ID_SUFFIX = import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || ''
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN || ''

const client = new SoraClient(
SORA_SIGNALING_URL,
SORA_CHANNEL_ID_PREFIX,
SORA_CHANNEL_ID_SUFFIX,
ACCESS_TOKEN,
)

// SDK バージョンの表示
const sdkVersionElement = document.querySelector('#sdk-version')
if (sdkVersionElement) {
sdkVersionElement.textContent = `${Sora.version()}`
}

document.querySelector('#connect')?.addEventListener('click', async () => {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
await client.connect(stream)
})

document.querySelector('#disconnect')?.addEventListener('click', async () => {
await client.disconnect()
})

document.querySelector('#disconnect-api')?.addEventListener('click', async () => {
await client.apiDisconnect()
})

document.querySelector('#get-stats')?.addEventListener('click', async () => {
const statsReport = await client.getStats()
const statsDiv = document.querySelector('#stats-report') as HTMLElement
const statsReportJsonDiv = document.querySelector('#stats-report-json')
if (statsDiv && statsReportJsonDiv) {
let statsHtml = ''
const statsReportJson: Record<string, unknown>[] = []
for (const report of statsReport.values()) {
statsHtml += `<h3>Type: ${report.type}</h3><ul>`
const reportJson: Record<string, unknown> = { id: report.id, type: report.type }
for (const [key, value] of Object.entries(report)) {
if (key !== 'type' && key !== 'id') {
statsHtml += `<li><strong>${key}:</strong> ${value}</li>`
reportJson[key] = value
}
}
statsHtml += '</ul>'
statsReportJson.push(reportJson)
}
statsDiv.innerHTML = statsHtml
// データ属性としても保存(オプション)
statsDiv.dataset.statsReportJson = JSON.stringify(statsReportJson)
}
})
})

class SoraClient {
private debug = false
private channelId: string
private metadata: { access_token: string }
private options: ConnectionOptions = {
dataChannelSignaling: true,
ignoreDisconnectWebSocket: true,
}

private sora: SoraConnection
private connection: ConnectionPublisher

constructor(
signaling_url: string,
channel_id_prefix: string,
channel_id_suffix: string,
access_token: string,
) {
this.sora = Sora.connection(signaling_url, this.debug)

// channel_id の生成
this.channelId = `${channel_id_prefix}sendonly_recvonly${channel_id_suffix}`
// access_token を指定する metadata の生成
this.metadata = { access_token: access_token }

this.connection = this.sora.sendonly(this.channelId, this.metadata, this.options)
this.connection.on('notify', this.onNotify.bind(this))

// E2E テスト用のコード
this.connection.on('signaling', this.onSignaling.bind(this))
}

async connect(stream: MediaStream): Promise<void> {
await this.connection.connect(stream)

const videoElement = document.querySelector<HTMLVideoElement>('#local-video')
if (videoElement !== null) {
videoElement.srcObject = stream
}
}

async disconnect(): Promise<void> {
await this.connection.disconnect()

const videoElement = document.querySelector<HTMLVideoElement>('#local-video')
if (videoElement !== null) {
videoElement.srcObject = null
}
}

getStats(): Promise<RTCStatsReport> {
if (this.connection.pc === null) {
return Promise.reject(new Error('PeerConnection is not ready'))
}
return this.connection.pc.getStats()
}

private onNotify(event: SignalingNotifyMessage): void {
if (
event.event_type === 'connection.created' &&
this.connection.connectionId === event.connection_id
) {
const connectionIdElement = document.querySelector('#connection-id')
if (connectionIdElement) {
connectionIdElement.textContent = event.connection_id
}
}
}

// E2E テスト用のコード
private onSignaling(event: SignalingEvent): void {
if (event.type === 'onmessage-switched') {
console.log('[signaling]', event.type, event.transportType)
}
if (event.type === 'onmessage-close') {
console.log('[signaling]', event.type, event.transportType)
}
}

// E2E テスト側で実行した方が良い気がする
async apiDisconnect(): Promise<void> {
const apiUrl = import.meta.env.VITE_SORA_API_URL
if (apiUrl === '') {
console.error('VITE_SORA_API_URL is not set')
}
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sora-Target': 'Sora_20151104.DisconnectConnection',
},
body: JSON.stringify({
channel_id: this.channelId,
connection_id: this.connection.connectionId,
}),
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
}
}
4 changes: 4 additions & 0 deletions playwright.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export default defineConfig({
use: {
launchOptions: {
args: [
// CORS 無効
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',

'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
// "--use-file-for-fake-video-capture=/app/sample.mjpeg",
Expand Down
Loading

0 comments on commit 98161dd

Please sign in to comment.