1import { ExpoConfig } from '@expo/config';
2import { Middleware } from 'metro-config';
3
4import { createDebuggerTelemetryMiddleware, findDebugTool } from '../metroDebuggerMiddleware';
5import { logEventAsync } from '../rudderstackClient';
6
7jest.mock('../getMetroDebugProperties');
8jest.mock('../rudderstackClient');
9
10const FLIPPER_UA = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Flipper/0.177.0 Chrome/100.0.4896.143 Electron/18.2.0 Safari/537.36`;
11const CHROME_ORIGIN = `https://chrome-devtools-frontend.appspot.com`;
12
13const fakeExpoConfig = {
14  sdkVersion: '47.0.0',
15  jsEngine: 'hermes',
16} as ExpoConfig;
17
18/** Create a fake request object, based on the provided options */
19const req = (options: { url: string; userAgent?: string; origin?: string }) =>
20  ({
21    url: options.url,
22    headers: {
23      'user-agent': options.userAgent,
24      origin: options.origin,
25    },
26  } as Parameters<Middleware>[0]);
27
28describe(findDebugTool, () => {
29  it('returns flipper from user agent', () => {
30    expect(findDebugTool(req({ url: '/json', userAgent: FLIPPER_UA }))).toMatchObject({
31      name: 'flipper',
32      version: '0.177.0',
33    });
34  });
35
36  it('returns chrome from origin', () => {
37    expect(findDebugTool(req({ url: '/index.map', origin: CHROME_ORIGIN }))).toMatchObject({
38      name: 'chrome',
39    });
40  });
41});
42
43describe(createDebuggerTelemetryMiddleware, () => {
44  it('reports known tool from user agent', () => {
45    const middleware = createDebuggerTelemetryMiddleware('/fake-project', fakeExpoConfig);
46    const next = jest.fn();
47
48    middleware(req({ url: '/json', userAgent: FLIPPER_UA }), {} as any, next);
49
50    expect(logEventAsync).toHaveBeenCalled();
51  });
52
53  it('only reports known tool once', () => {
54    const middleware = createDebuggerTelemetryMiddleware('/fake-project', fakeExpoConfig);
55    const next = jest.fn();
56
57    middleware(req({ url: '/json', userAgent: FLIPPER_UA }), {} as any, next);
58    middleware(req({ url: '/json', userAgent: FLIPPER_UA }), {} as any, next);
59
60    expect(logEventAsync).toHaveBeenCalledTimes(1);
61    expect(next).toHaveBeenCalledTimes(2);
62  });
63
64  it('does not report with unknown user agent', () => {
65    const middleware = createDebuggerTelemetryMiddleware('/fake-project', fakeExpoConfig);
66    const next = jest.fn();
67
68    middleware(req({ url: '/json', userAgent: 'unknown/4.2.0' }), {} as any, next);
69
70    expect(logEventAsync).not.toHaveBeenCalled();
71  });
72
73  it('does not report when telemetry is turned off', () => {
74    process.env.EXPO_NO_TELEMETRY = 'true';
75
76    const middleware = createDebuggerTelemetryMiddleware('/fake-project', fakeExpoConfig);
77    const next = jest.fn();
78
79    middleware(req({ url: '/json', userAgent: FLIPPER_UA }), {} as any, next);
80
81    expect(logEventAsync).not.toHaveBeenCalled();
82
83    delete process.env.EXPO_NO_TELEMETRY;
84  });
85
86  it('does not report when app is not using hermes', () => {
87    const expoConfig = { ...fakeExpoConfig, jsEngine: 'jsc' as const };
88    const middleware = createDebuggerTelemetryMiddleware('/fake-project', expoConfig);
89    const next = jest.fn();
90
91    middleware(req({ url: '/json', userAgent: FLIPPER_UA }), {} as any, next);
92
93    expect(logEventAsync).not.toHaveBeenCalled();
94  });
95});
96