xref: /expo/packages/jest-expo/src/index.js (revision 79f4e8ce)
1import { EventEmitter } from 'fbemitter';
2import { Linking } from 'react-native';
3
4const allOriginalPropertyDescriptors = new Map();
5
6export function mockProperty(object, property, mockValue) {
7  // Save a reference to the original property descriptor
8  if (!allOriginalPropertyDescriptors.has(object)) {
9    allOriginalPropertyDescriptors.set(object, new Map());
10  }
11  let descriptor = Object.getOwnPropertyDescriptor(object, property);
12  allOriginalPropertyDescriptors.get(object).set(property, descriptor);
13
14  // Select fields to inherit from the original descriptor
15  if (descriptor) {
16    const { configurable, enumerable, writable } = descriptor;
17    descriptor = { configurable, enumerable, writable };
18  }
19
20  Object.defineProperty(object, property, {
21    configurable: true,
22    enumerable: true,
23    writable: true,
24    ...descriptor,
25    value: mockValue,
26  });
27}
28
29export function unmockProperty(object, property) {
30  const descriptors = allOriginalPropertyDescriptors.get(object);
31  if (!descriptors || !descriptors.has(property)) {
32    return;
33  }
34
35  const descriptor = descriptors.get(property);
36  if (descriptor) {
37    Object.defineProperty(object, property, descriptor);
38  } else {
39    delete object[property];
40  }
41
42  // Clean up the reference
43  descriptors.delete(property);
44  if (!descriptors.size) {
45    allOriginalPropertyDescriptors.delete(object);
46  }
47}
48
49export function unmockAllProperties() {
50  for (const [object, descriptors] of allOriginalPropertyDescriptors) {
51    for (const [property, descriptor] of descriptors) {
52      if (descriptor) {
53        Object.defineProperty(object, property, descriptor);
54      } else {
55        delete object[property];
56      }
57    }
58  }
59  allOriginalPropertyDescriptors.clear();
60}
61
62export function mockLinking() {
63  const emitter = new EventEmitter();
64  const subscriptions = {};
65
66  mockProperty(
67    Linking,
68    'addEventListener',
69    jest.fn((type, cb) => {
70      const subscription = emitter.addListener(type, cb);
71      subscriptions[type] = subscriptions[type] || new Map();
72      subscriptions[type].set(cb, subscription);
73      return subscription;
74    })
75  );
76
77  mockProperty(
78    Linking,
79    'removeEventListener',
80    jest.fn((type, cb) => {
81      subscriptions[type].delete(cb);
82    })
83  );
84
85  return (type, data) => {
86    emitter.emit(type, data);
87  };
88}
89