@thumbmarkjs/thumbmarkjs 1.7.6 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,120 @@
1
+ import getMediaDevices from './index';
2
+
3
+ describe('mediaDevices component tests', () => {
4
+ let originalMediaDevices: MediaDevices | undefined;
5
+
6
+ beforeAll(() => {
7
+ originalMediaDevices = navigator.mediaDevices;
8
+ });
9
+
10
+ afterAll(() => {
11
+ Object.defineProperty(navigator, 'mediaDevices', {
12
+ value: originalMediaDevices,
13
+ configurable: true,
14
+ });
15
+ });
16
+
17
+ function mockEnumerateDevices(devices: Array<{ kind: string }>) {
18
+ Object.defineProperty(navigator, 'mediaDevices', {
19
+ value: {
20
+ enumerateDevices: jest.fn().mockResolvedValue(devices),
21
+ },
22
+ configurable: true,
23
+ });
24
+ }
25
+
26
+ test('returns valid structure when API is available', async () => {
27
+ mockEnumerateDevices([
28
+ { kind: 'audioinput' },
29
+ { kind: 'audiooutput' },
30
+ { kind: 'videoinput' },
31
+ ]);
32
+
33
+ const result = await getMediaDevices();
34
+
35
+ expect(result).toEqual({
36
+ audioinput: 1,
37
+ audiooutput: 1,
38
+ videoinput: 1,
39
+ });
40
+ });
41
+
42
+ test('returns null when mediaDevices API is unavailable', async () => {
43
+ Object.defineProperty(navigator, 'mediaDevices', {
44
+ value: undefined,
45
+ configurable: true,
46
+ });
47
+
48
+ const result = await getMediaDevices();
49
+ expect(result).toBeNull();
50
+ });
51
+
52
+ test('returns null when enumerateDevices throws', async () => {
53
+ Object.defineProperty(navigator, 'mediaDevices', {
54
+ value: {
55
+ enumerateDevices: jest.fn().mockRejectedValue(new Error('NotAllowedError')),
56
+ },
57
+ configurable: true,
58
+ });
59
+
60
+ const result = await getMediaDevices();
61
+ expect(result).toBeNull();
62
+ });
63
+
64
+ test('handles empty device list', async () => {
65
+ mockEnumerateDevices([]);
66
+
67
+ const result = await getMediaDevices();
68
+
69
+ expect(result).toEqual({
70
+ audioinput: 0,
71
+ audiooutput: 0,
72
+ videoinput: 0,
73
+ });
74
+ });
75
+
76
+ test('counts multiple devices of same kind', async () => {
77
+ mockEnumerateDevices([
78
+ { kind: 'videoinput' },
79
+ { kind: 'videoinput' },
80
+ { kind: 'audioinput' },
81
+ ]);
82
+
83
+ const result = await getMediaDevices();
84
+
85
+ expect(result).toEqual({
86
+ audioinput: 1,
87
+ audiooutput: 0,
88
+ videoinput: 2,
89
+ });
90
+ });
91
+
92
+ test('ignores unknown device kind values', async () => {
93
+ mockEnumerateDevices([
94
+ { kind: 'audioinput' },
95
+ { kind: '' },
96
+ { kind: 'somethingelse' },
97
+ { kind: 'videoinput' },
98
+ ]);
99
+
100
+ const result = await getMediaDevices();
101
+
102
+ expect(result).toEqual({
103
+ audioinput: 1,
104
+ audiooutput: 0,
105
+ videoinput: 1,
106
+ });
107
+ });
108
+
109
+ test('returns null when enumerateDevices resolves with null', async () => {
110
+ Object.defineProperty(navigator, 'mediaDevices', {
111
+ value: {
112
+ enumerateDevices: jest.fn().mockResolvedValue(null),
113
+ },
114
+ configurable: true,
115
+ });
116
+
117
+ const result = await getMediaDevices();
118
+ expect(result).toBeNull();
119
+ });
120
+ });
@@ -0,0 +1,26 @@
1
+ import { componentInterface } from '../../factory';
2
+
3
+ export default async function getMediaDevices(): Promise<componentInterface | null> {
4
+ if (typeof navigator === 'undefined' ||
5
+ !navigator.mediaDevices ||
6
+ typeof navigator.mediaDevices.enumerateDevices !== 'function') {
7
+ return null;
8
+ }
9
+
10
+ try {
11
+ const devices = await navigator.mediaDevices.enumerateDevices();
12
+
13
+ const counts: Record<string, number> = {};
14
+ for (const device of devices) {
15
+ counts[device.kind] = (counts[device.kind] || 0) + 1;
16
+ }
17
+
18
+ return {
19
+ audioinput: counts['audioinput'] || 0,
20
+ audiooutput: counts['audiooutput'] || 0,
21
+ videoinput: counts['videoinput'] || 0,
22
+ };
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
package/src/factory.ts CHANGED
@@ -21,6 +21,8 @@ import getSystem from "./components/system";
21
21
  import getWebGL from "./components/webgl";
22
22
 
23
23
  // Import experimental component functions
24
+ import getIntl from "./components/intl";
25
+ import getMediaDevices from "./components/mediaDevices";
24
26
  import getWebRTC from "./components/webrtc";
25
27
  import getMathML from "./components/mathml";
26
28
  import getSpeech from "./components/speech";
@@ -48,7 +50,9 @@ export const tm_component_promises = {
48
50
  * @description key->function map of experimental components. Only resolved during logging.
49
51
  */
50
52
  export const tm_experimental_component_promises = {
53
+ 'intl': getIntl,
51
54
  'mathml': getMathML,
55
+ 'mediadevices': getMediaDevices,
52
56
  };
53
57
 
54
58
  // the component interface is the form of the JSON object the function's promise must return