diff --git a/package.json b/package.json
index 9670526..d983793 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"build": "react-scripts --optimize-for-size build",
"test": "react-scripts test",
"test-ci": "react-scripts test --watchAll=false",
+ "test-coverage": "react-scripts test --watchAll=false --coverage",
"eject": "react-scripts eject",
"i18n-extract": "lingui extract",
"i18n-extract:clean": "lingui extract --clean",
diff --git a/src/utils/testing.js b/src/utils/testing.js
index d92536d..2ed72c9 100644
--- a/src/utils/testing.js
+++ b/src/utils/testing.js
@@ -2,6 +2,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
+import { HashRouter as DOMRouter } from 'react-router-dom';
import theme from '../theme';
import I18n from '../I18n';
@@ -9,7 +10,9 @@ const AllTheProviders = ({ children }) => {
return (
- {children}
+
+ {children}
+
);
diff --git a/src/views/Edit/Sources/Network.test.js b/src/views/Edit/Sources/Network.test.js
index 89f125b..9e4f3fb 100644
--- a/src/views/Edit/Sources/Network.test.js
+++ b/src/views/Edit/Sources/Network.test.js
@@ -1,22 +1,56 @@
import React from 'react';
-import { render, fireEvent } from '../../../utils/testing';
+import { render, fireEvent, screen } from '../../../utils/testing';
import '@testing-library/jest-dom';
import * as Network from './Network';
-test('FFmpeg 5: RTSP with -timeout', async () => {
- let $settings = {};
+const $skills_ffmpeg5 = {
+ ffmpeg: {
+ version: '5.1.2',
+ },
+ formats: {
+ demuxers: ['rtsp'],
+ },
+ protocols: {
+ input: ['http', 'https', 'rtmp', 'rtmps', 'srt'],
+ },
+};
+
+const $skills_ffmpeg4 = {
+ ffmpeg: {
+ version: '4.4.1',
+ },
+ formats: {
+ demuxers: ['rtsp'],
+ },
+ protocols: {
+ input: ['http', 'https', 'rtmp', 'rtmps', 'srt'],
+ },
+};
+
+const $config = {
+ rtmp: {
+ enabled: true,
+ app: '/live',
+ token: 'foobar',
+ },
+ srt: {
+ enabled: true,
+ token: 'foobar',
+ passphrase: 'bazfoobazfoo',
+ },
+};
+
+test('source:network pull', async () => {
+ let $settings = {
+ mode: 'pull',
+ };
const handleChange = (settings) => {
$settings = settings;
};
- let $inputs = [];
- const handleProbe = (_, inputs) => {
- $inputs = inputs;
- };
-
const Source = Network.component;
- let { getByLabelText, getByText, queryByText, getByRole, rerender } = render();
+ let { getByLabelText, queryByText, rerender } = render();
const input = getByLabelText('Address');
fireEvent.change(input, { target: { value: 'rtsp://127.0.0.1/live/stream' } });
@@ -24,30 +58,269 @@ test('FFmpeg 5: RTSP with -timeout', async () => {
expect($settings.mode).toBe('pull');
expect($settings.address).toBe('rtsp://127.0.0.1/live/stream');
- rerender();
+ expect(queryByText(`This protocol is unknown or not supported by the available FFmpeg binary.`)).toBeInTheDocument();
- expect(queryByText('This protocol is unknown or not supported by the available FFmpeg binary.')).toBeInTheDocument();
+ rerender();
- const $skills = {
- ffmpeg: {
- version: '5.1.2',
+ expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBe(null);
+});
+
+const pullmatrix = {
+ settings: {
+ mode: 'pull',
+ address: '',
+ username: 'admin',
+ password: 'foobar',
+ rtsp: {
+ udp: false,
+ stimeout: 5000000,
},
- formats: {
- demuxers: ['rtsp'],
+ http: {
+ readNative: true,
+ forceFramerate: true,
+ framerate: 25,
+ userAgent: 'foobaz/1',
},
+ general: {
+ fflags: ['genpts'],
+ thread_queue_size: 512,
+ },
+ },
+ tests: [],
+};
+
+pullmatrix.tests = [
+ {
+ name: 'RTSP',
+ settings: { ...pullmatrix.settings, address: 'rtsp://127.0.0.1/live/stream' },
+ skills: $skills_ffmpeg4,
+ input: {
+ address: 'rtsp://admin:foobar@127.0.0.1/live/stream',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-stimeout', 5000000, '-rtsp_transport', 'tcp'],
+ },
+ },
+ {
+ name: 'RTMP',
+ settings: { ...pullmatrix.settings, address: 'rtmp://127.0.0.1/live/stream' },
+ skills: $skills_ffmpeg4,
+ input: {
+ address: 'rtmp://admin:foobar@127.0.0.1/live/stream',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '3000000'],
+ },
+ },
+ {
+ name: 'HTTP',
+ settings: { ...pullmatrix.settings, address: 'http://127.0.0.1/live/stream.m3u8' },
+ skills: $skills_ffmpeg4,
+ input: {
+ address: 'http://admin:foobar@127.0.0.1/live/stream.m3u8',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '20000000', '-re', '-r', 25, '-user_agent', 'foobaz/1'],
+ },
+ },
+ {
+ name: 'SRT',
+ settings: { ...pullmatrix.settings, address: 'srt://127.0.0.1?mode=caller&streamid=foobar' },
+ skills: $skills_ffmpeg4,
+ input: {
+ address: 'srt://127.0.0.1?mode=caller&streamid=foobar',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512],
+ },
+ },
+ {
+ name: 'RTSP',
+ settings: { ...pullmatrix.settings, address: 'rtsp://127.0.0.1/live/stream' },
+ skills: $skills_ffmpeg5,
+ input: {
+ address: 'rtsp://admin:foobar@127.0.0.1/live/stream',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-timeout', 5000000, '-rtsp_transport', 'tcp'],
+ },
+ },
+ {
+ name: 'RTMP',
+ settings: { ...pullmatrix.settings, address: 'rtmp://127.0.0.1/live/stream' },
+ skills: $skills_ffmpeg5,
+ input: {
+ address: 'rtmp://admin:foobar@127.0.0.1/live/stream',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '3000000'],
+ },
+ },
+ {
+ name: 'HTTP',
+ settings: { ...pullmatrix.settings, address: 'http://127.0.0.1/live/stream.m3u8' },
+ skills: $skills_ffmpeg5,
+ input: {
+ address: 'http://admin:foobar@127.0.0.1/live/stream.m3u8',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '20000000', '-re', '-r', 25, '-user_agent', 'foobaz/1'],
+ },
+ },
+ {
+ name: 'SRT',
+ settings: { ...pullmatrix.settings, address: 'srt://127.0.0.1?mode=caller&streamid=foobar' },
+ skills: $skills_ffmpeg5,
+ input: {
+ address: 'srt://127.0.0.1?mode=caller&streamid=foobar',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512],
+ },
+ },
+];
+
+test.each(pullmatrix.tests)('source:network pull $name input with ffmpeg $skills.ffmpeg.version', async (data) => {
+ let $inputs = [];
+ const handleProbe = (_, inputs) => {
+ $inputs = inputs;
};
- rerender();
+ const Source = Network.component;
+
+ let { getByText, getByRole } = render();
- expect(queryByText('This protocol is unknown or not supported by the available FFmpeg binary.')).toBe(null);
expect(getByText('Probe')).toBeInTheDocument();
const button = getByRole('button', { name: 'Probe' });
fireEvent.click(button, { bubbles: true });
expect($inputs.length).toBe(1);
- expect($inputs[0]).toStrictEqual({
- address: 'rtsp://127.0.0.1/live/stream',
- options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-timeout', 5000000, '-rtsp_transport', 'tcp'],
- });
+ expect($inputs[0]).toStrictEqual(data.input);
+});
+
+test('source:network push', async () => {
+ let $settings = {
+ mode: 'push',
+ push: {
+ type: 'rtmp',
+ },
+ };
+ const handleChange = (settings) => {
+ $settings = settings;
+ };
+
+ const Source = Network.component;
+ let { queryByText, rerender } = render();
+
+ expect($settings.mode).toBe('push');
+
+ expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBeInTheDocument();
+
+ rerender();
+
+ expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBe(null);
+});
+
+test('source:network push RTMP', async () => {
+ let $settings = {
+ mode: 'push',
+ push: {
+ type: 'rtmp',
+ },
+ };
+ const handleChange = (settings) => {
+ $settings = settings;
+ };
+
+ const Source = Network.component;
+ let { getByText, queryByText, rerender } = render();
+
+ expect($settings.mode).toBe('push');
+ expect($settings.push.type).toBe('rtmp');
+
+ expect(queryByText(`Enable RTMP server ...`)).toBeInTheDocument();
+
+ rerender();
+
+ expect(getByText('Probe')).toBeInTheDocument();
+});
+
+test('source:network push SRT', async () => {
+ let $settings = {
+ mode: 'push',
+ push: {
+ type: 'srt',
+ },
+ };
+ const handleChange = (settings) => {
+ $settings = settings;
+ };
+
+ const Source = Network.component;
+ let { getByText, queryByText, rerender } = render();
+
+ expect($settings.mode).toBe('push');
+ expect($settings.push.type).toBe('srt');
+
+ expect(queryByText(`Enable SRT server ...`)).toBeInTheDocument();
+
+ rerender();
+
+ expect(getByText('Probe')).toBeInTheDocument();
+});
+
+const pushmatrix = {
+ settings: {
+ mode: 'push',
+ push: {
+ type: '',
+ },
+ },
+ tests: [],
+};
+
+pushmatrix.tests = [
+ {
+ name: 'RTMP',
+ settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'rtmp' } },
+ skills: $skills_ffmpeg4,
+ config: $config,
+ input: {
+ address: 'rtmp://localhost/live/external.stream?token=foobar',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '3000000'],
+ },
+ },
+ {
+ name: 'SRT',
+ settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'srt' } },
+ skills: $skills_ffmpeg4,
+ config: $config,
+ input: {
+ address: 'srt://localhost?mode=caller&transtype=live&streamid=external,mode:request,token:foobar&passphrase=bazfoobazfoo',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512],
+ },
+ },
+ {
+ name: 'RTMP',
+ settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'rtmp' } },
+ skills: $skills_ffmpeg5,
+ config: $config,
+ input: {
+ address: 'rtmp://localhost/live/external.stream?token=foobar',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', '3000000'],
+ },
+ },
+ {
+ name: 'SRT',
+ settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'srt' } },
+ skills: $skills_ffmpeg5,
+ config: $config,
+ input: {
+ address: 'srt://localhost?mode=caller&transtype=live&streamid=external,mode:request,token:foobar&passphrase=bazfoobazfoo',
+ options: ['-fflags', '+genpts', '-thread_queue_size', 512],
+ },
+ },
+];
+
+test.each(pushmatrix.tests)('source:network push $name input with ffmpeg $skills.ffmpeg.version', async (data) => {
+ let $inputs = [];
+ const handleProbe = (_, inputs) => {
+ $inputs = inputs;
+ };
+
+ const Source = Network.component;
+ let { getByText, getByRole } = render();
+
+ expect(getByText('Probe')).toBeInTheDocument();
+
+ const button = getByRole('button', { name: 'Probe' });
+ fireEvent.click(button, { bubbles: true });
+
+ expect($inputs.length).toBe(1);
+ expect($inputs[0]).toStrictEqual(data.input);
});