tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DeviceRequestPrompt.test.ts (16230B)


      1 /**
      2 * @license
      3 * Copyright 2022 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 import {describe, it} from 'node:test';
      8 
      9 import expect from 'expect';
     10 
     11 import type {CDPSessionEvents} from '../api/CDPSession.js';
     12 import {TimeoutError} from '../common/Errors.js';
     13 import {EventEmitter} from '../common/EventEmitter.js';
     14 import {TimeoutSettings} from '../common/TimeoutSettings.js';
     15 
     16 import {
     17  DeviceRequestPrompt,
     18  DeviceRequestPromptDevice,
     19  DeviceRequestPromptManager,
     20 } from './DeviceRequestPrompt.js';
     21 
     22 class MockCDPSession extends EventEmitter<CDPSessionEvents> {
     23  async send(): Promise<any> {}
     24  connection() {
     25    return undefined;
     26  }
     27  readonly detached = false;
     28  async detach() {}
     29  id() {
     30    return '1';
     31  }
     32  parentSession() {
     33    return undefined;
     34  }
     35 }
     36 
     37 describe('DeviceRequestPrompt', function () {
     38  describe('waitForDevicePrompt', function () {
     39    it('should return prompt', async () => {
     40      const client = new MockCDPSession();
     41      const timeoutSettings = new TimeoutSettings();
     42      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
     43 
     44      const [prompt] = await Promise.all([
     45        manager.waitForDevicePrompt(),
     46        (() => {
     47          client.emit('DeviceAccess.deviceRequestPrompted', {
     48            id: '00000000000000000000000000000000',
     49            devices: [],
     50          });
     51        })(),
     52      ]);
     53      expect(prompt).toBeTruthy();
     54    });
     55 
     56    it('should respect timeout', async () => {
     57      const client = new MockCDPSession();
     58      const timeoutSettings = new TimeoutSettings();
     59      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
     60 
     61      await expect(
     62        manager.waitForDevicePrompt({timeout: 1}),
     63      ).rejects.toBeInstanceOf(TimeoutError);
     64    });
     65 
     66    it('should respect default timeout when there is no custom timeout', async () => {
     67      const client = new MockCDPSession();
     68      const timeoutSettings = new TimeoutSettings();
     69      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
     70 
     71      timeoutSettings.setDefaultTimeout(1);
     72      await expect(manager.waitForDevicePrompt()).rejects.toBeInstanceOf(
     73        TimeoutError,
     74      );
     75    });
     76 
     77    it('should prioritize exact timeout over default timeout', async () => {
     78      const client = new MockCDPSession();
     79      const timeoutSettings = new TimeoutSettings();
     80      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
     81 
     82      timeoutSettings.setDefaultTimeout(0);
     83      await expect(
     84        manager.waitForDevicePrompt({timeout: 1}),
     85      ).rejects.toBeInstanceOf(TimeoutError);
     86    });
     87 
     88    it('should work with no timeout', async () => {
     89      const client = new MockCDPSession();
     90      const timeoutSettings = new TimeoutSettings();
     91      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
     92 
     93      const [prompt] = await Promise.all([
     94        manager.waitForDevicePrompt({timeout: 0}),
     95        (async () => {
     96          await new Promise(resolve => {
     97            setTimeout(resolve, 50);
     98          });
     99          client.emit('DeviceAccess.deviceRequestPrompted', {
    100            id: '00000000000000000000000000000000',
    101            devices: [],
    102          });
    103        })(),
    104      ]);
    105      expect(prompt).toBeTruthy();
    106    });
    107 
    108    it('should return the same prompt when there are many watchdogs simultaneously', async () => {
    109      const client = new MockCDPSession();
    110      const timeoutSettings = new TimeoutSettings();
    111      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
    112 
    113      const [prompt1, prompt2] = await Promise.all([
    114        manager.waitForDevicePrompt(),
    115        manager.waitForDevicePrompt(),
    116        (() => {
    117          client.emit('DeviceAccess.deviceRequestPrompted', {
    118            id: '00000000000000000000000000000000',
    119            devices: [],
    120          });
    121        })(),
    122      ]);
    123      expect(prompt1 === prompt2).toBeTruthy();
    124    });
    125 
    126    it('should listen and shortcut when there are no watchdogs', async () => {
    127      const client = new MockCDPSession();
    128      const timeoutSettings = new TimeoutSettings();
    129      const manager = new DeviceRequestPromptManager(client, timeoutSettings);
    130 
    131      client.emit('DeviceAccess.deviceRequestPrompted', {
    132        id: '00000000000000000000000000000000',
    133        devices: [],
    134      });
    135 
    136      expect(manager).toBeTruthy();
    137    });
    138  });
    139 
    140  describe('DeviceRequestPrompt.devices', function () {
    141    it('lists devices as they arrive', function () {
    142      const client = new MockCDPSession();
    143      const timeoutSettings = new TimeoutSettings();
    144      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    145        id: '00000000000000000000000000000000',
    146        devices: [],
    147      });
    148 
    149      expect(prompt.devices).toHaveLength(0);
    150      client.emit('DeviceAccess.deviceRequestPrompted', {
    151        id: '00000000000000000000000000000000',
    152        devices: [{id: '00000000', name: 'Device 0'}],
    153      });
    154      expect(prompt.devices).toHaveLength(1);
    155      client.emit('DeviceAccess.deviceRequestPrompted', {
    156        id: '00000000000000000000000000000000',
    157        devices: [
    158          {id: '00000000', name: 'Device 0'},
    159          {id: '11111111', name: 'Device 1'},
    160        ],
    161      });
    162      expect(prompt.devices).toHaveLength(2);
    163      expect(prompt.devices[0]).toBeInstanceOf(DeviceRequestPromptDevice);
    164      expect(prompt.devices[1]).toBeInstanceOf(DeviceRequestPromptDevice);
    165    });
    166 
    167    it('does not list devices from events of another prompt', function () {
    168      const client = new MockCDPSession();
    169      const timeoutSettings = new TimeoutSettings();
    170      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    171        id: '00000000000000000000000000000000',
    172        devices: [],
    173      });
    174 
    175      expect(prompt.devices).toHaveLength(0);
    176      client.emit('DeviceAccess.deviceRequestPrompted', {
    177        id: '88888888888888888888888888888888',
    178        devices: [
    179          {id: '00000000', name: 'Device 0'},
    180          {id: '11111111', name: 'Device 1'},
    181        ],
    182      });
    183      expect(prompt.devices).toHaveLength(0);
    184    });
    185  });
    186 
    187  describe('DeviceRequestPrompt.waitForDevice', function () {
    188    it('should return first matching device', async () => {
    189      const client = new MockCDPSession();
    190      const timeoutSettings = new TimeoutSettings();
    191      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    192        id: '00000000000000000000000000000000',
    193        devices: [],
    194      });
    195 
    196      const [device] = await Promise.all([
    197        prompt.waitForDevice(({name}) => {
    198          return name.includes('1');
    199        }),
    200        (() => {
    201          client.emit('DeviceAccess.deviceRequestPrompted', {
    202            id: '00000000000000000000000000000000',
    203            devices: [{id: '00000000', name: 'Device 0'}],
    204          });
    205          client.emit('DeviceAccess.deviceRequestPrompted', {
    206            id: '00000000000000000000000000000000',
    207            devices: [
    208              {id: '00000000', name: 'Device 0'},
    209              {id: '11111111', name: 'Device 1'},
    210            ],
    211          });
    212        })(),
    213      ]);
    214      expect(device).toBeInstanceOf(DeviceRequestPromptDevice);
    215    });
    216 
    217    it('should return first matching device from already known devices', async () => {
    218      const client = new MockCDPSession();
    219      const timeoutSettings = new TimeoutSettings();
    220      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    221        id: '00000000000000000000000000000000',
    222        devices: [
    223          {id: '00000000', name: 'Device 0'},
    224          {id: '11111111', name: 'Device 1'},
    225        ],
    226      });
    227 
    228      const device = await prompt.waitForDevice(({name}) => {
    229        return name.includes('1');
    230      });
    231      expect(device).toBeInstanceOf(DeviceRequestPromptDevice);
    232    });
    233 
    234    it('should return device in the devices list', async () => {
    235      const client = new MockCDPSession();
    236      const timeoutSettings = new TimeoutSettings();
    237      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    238        id: '00000000000000000000000000000000',
    239        devices: [],
    240      });
    241 
    242      const [device] = await Promise.all([
    243        prompt.waitForDevice(({name}) => {
    244          return name.includes('1');
    245        }),
    246        (() => {
    247          client.emit('DeviceAccess.deviceRequestPrompted', {
    248            id: '00000000000000000000000000000000',
    249            devices: [
    250              {id: '00000000', name: 'Device 0'},
    251              {id: '11111111', name: 'Device 1'},
    252            ],
    253          });
    254        })(),
    255      ]);
    256      expect(prompt.devices).toContain(device);
    257    });
    258 
    259    it('should respect timeout', async () => {
    260      const client = new MockCDPSession();
    261      const timeoutSettings = new TimeoutSettings();
    262      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    263        id: '00000000000000000000000000000000',
    264        devices: [],
    265      });
    266 
    267      await expect(
    268        prompt.waitForDevice(
    269          ({name}) => {
    270            return name.includes('Device');
    271          },
    272          {timeout: 1},
    273        ),
    274      ).rejects.toBeInstanceOf(TimeoutError);
    275    });
    276 
    277    it('should respect default timeout when there is no custom timeout', async () => {
    278      const client = new MockCDPSession();
    279      const timeoutSettings = new TimeoutSettings();
    280      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    281        id: '00000000000000000000000000000000',
    282        devices: [],
    283      });
    284 
    285      timeoutSettings.setDefaultTimeout(1);
    286      await expect(
    287        prompt.waitForDevice(
    288          ({name}) => {
    289            return name.includes('Device');
    290          },
    291          {timeout: 1},
    292        ),
    293      ).rejects.toBeInstanceOf(TimeoutError);
    294    });
    295 
    296    it('should prioritize exact timeout over default timeout', async () => {
    297      const client = new MockCDPSession();
    298      const timeoutSettings = new TimeoutSettings();
    299      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    300        id: '00000000000000000000000000000000',
    301        devices: [],
    302      });
    303 
    304      timeoutSettings.setDefaultTimeout(0);
    305      await expect(
    306        prompt.waitForDevice(
    307          ({name}) => {
    308            return name.includes('Device');
    309          },
    310          {timeout: 1},
    311        ),
    312      ).rejects.toBeInstanceOf(TimeoutError);
    313    });
    314 
    315    it('should work with no timeout', async () => {
    316      const client = new MockCDPSession();
    317      const timeoutSettings = new TimeoutSettings();
    318      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    319        id: '00000000000000000000000000000000',
    320        devices: [],
    321      });
    322 
    323      const [device] = await Promise.all([
    324        prompt.waitForDevice(
    325          ({name}) => {
    326            return name.includes('1');
    327          },
    328          {timeout: 0},
    329        ),
    330        (() => {
    331          client.emit('DeviceAccess.deviceRequestPrompted', {
    332            id: '00000000000000000000000000000000',
    333            devices: [{id: '00000000', name: 'Device 0'}],
    334          });
    335          client.emit('DeviceAccess.deviceRequestPrompted', {
    336            id: '00000000000000000000000000000000',
    337            devices: [
    338              {id: '00000000', name: 'Device 0'},
    339              {id: '11111111', name: 'Device 1'},
    340            ],
    341          });
    342        })(),
    343      ]);
    344      expect(device).toBeInstanceOf(DeviceRequestPromptDevice);
    345    });
    346 
    347    it('should be able to abort', async () => {
    348      const client = new MockCDPSession();
    349      const timeoutSettings = new TimeoutSettings();
    350      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    351        id: '00000000000000000000000000000000',
    352        devices: [],
    353      });
    354      const abortController = new AbortController();
    355 
    356      const task = prompt.waitForDevice(
    357        () => {
    358          return false;
    359        },
    360        {signal: abortController.signal},
    361      );
    362      abortController.abort();
    363      await expect(task).rejects.toThrow(/aborted/);
    364    });
    365 
    366    it('should return same device from multiple watchdogs', async () => {
    367      const client = new MockCDPSession();
    368      const timeoutSettings = new TimeoutSettings();
    369      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    370        id: '00000000000000000000000000000000',
    371        devices: [],
    372      });
    373 
    374      const [device1, device2] = await Promise.all([
    375        prompt.waitForDevice(({name}) => {
    376          return name.includes('1');
    377        }),
    378        prompt.waitForDevice(({name}) => {
    379          return name.includes('1');
    380        }),
    381        (() => {
    382          client.emit('DeviceAccess.deviceRequestPrompted', {
    383            id: '00000000000000000000000000000000',
    384            devices: [{id: '00000000', name: 'Device 0'}],
    385          });
    386          client.emit('DeviceAccess.deviceRequestPrompted', {
    387            id: '00000000000000000000000000000000',
    388            devices: [
    389              {id: '00000000', name: 'Device 0'},
    390              {id: '11111111', name: 'Device 1'},
    391            ],
    392          });
    393        })(),
    394      ]);
    395      expect(device1 === device2).toBeTruthy();
    396    });
    397  });
    398 
    399  describe('DeviceRequestPrompt.select', function () {
    400    it('should succeed with listed device', async () => {
    401      const client = new MockCDPSession();
    402      const timeoutSettings = new TimeoutSettings();
    403      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    404        id: '00000000000000000000000000000000',
    405        devices: [],
    406      });
    407 
    408      const [device] = await Promise.all([
    409        prompt.waitForDevice(({name}) => {
    410          return name.includes('1');
    411        }),
    412        (() => {
    413          client.emit('DeviceAccess.deviceRequestPrompted', {
    414            id: '00000000000000000000000000000000',
    415            devices: [
    416              {id: '00000000', name: 'Device 0'},
    417              {id: '11111111', name: 'Device 1'},
    418            ],
    419          });
    420        })(),
    421      ]);
    422      await prompt.select(device);
    423    });
    424 
    425    it('should error for device not listed in devices', async () => {
    426      const client = new MockCDPSession();
    427      const timeoutSettings = new TimeoutSettings();
    428      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    429        id: '00000000000000000000000000000000',
    430        devices: [],
    431      });
    432 
    433      await expect(
    434        prompt.select(new DeviceRequestPromptDevice('11111111', 'Device 1')),
    435      ).rejects.toThrowError('Cannot select unknown device!');
    436    });
    437 
    438    it('should fail when selecting prompt twice', async () => {
    439      const client = new MockCDPSession();
    440      const timeoutSettings = new TimeoutSettings();
    441      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    442        id: '00000000000000000000000000000000',
    443        devices: [],
    444      });
    445 
    446      const [device] = await Promise.all([
    447        prompt.waitForDevice(({name}) => {
    448          return name.includes('1');
    449        }),
    450        (() => {
    451          client.emit('DeviceAccess.deviceRequestPrompted', {
    452            id: '00000000000000000000000000000000',
    453            devices: [
    454              {id: '00000000', name: 'Device 0'},
    455              {id: '11111111', name: 'Device 1'},
    456            ],
    457          });
    458        })(),
    459      ]);
    460      await prompt.select(device);
    461      await expect(prompt.select(device)).rejects.toThrowError(
    462        'Cannot select DeviceRequestPrompt which is already handled!',
    463      );
    464    });
    465  });
    466 
    467  describe('DeviceRequestPrompt.cancel', function () {
    468    it('should succeed on first call', async () => {
    469      const client = new MockCDPSession();
    470      const timeoutSettings = new TimeoutSettings();
    471      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    472        id: '00000000000000000000000000000000',
    473        devices: [],
    474      });
    475      await prompt.cancel();
    476    });
    477 
    478    it('should fail when canceling prompt twice', async () => {
    479      const client = new MockCDPSession();
    480      const timeoutSettings = new TimeoutSettings();
    481      const prompt = new DeviceRequestPrompt(client, timeoutSettings, {
    482        id: '00000000000000000000000000000000',
    483        devices: [],
    484      });
    485      await prompt.cancel();
    486      await expect(prompt.cancel()).rejects.toThrowError(
    487        'Cannot cancel DeviceRequestPrompt which is already handled!',
    488      );
    489    });
    490  });
    491 });