| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | * |
| 7 | * @emails react-core |
| 8 | */ |
| 9 | |
| 10 | 'use strict'; |
| 11 | |
| 12 | describe('EventPluginRegistry', () => { |
| 13 | let EventPluginRegistry; |
| 14 | let createPlugin; |
| 15 | |
| 16 | beforeEach(() => { |
| 17 | jest.resetModules(); |
| 18 | // These tests are intentionally testing the private injection interface. |
| 19 | // The public API surface of this is covered by other tests so |
| 20 | // if `EventPluginRegistry` is ever deleted, these tests should be |
| 21 | // safe to remove too. |
| 22 | EventPluginRegistry = require('react-native-renderer/src/legacy-events/EventPluginRegistry'); |
| 23 | |
| 24 | createPlugin = function (properties) { |
| 25 | return Object.assign({extractEvents: function () {}}, properties); |
| 26 | }; |
| 27 | }); |
| 28 | |
| 29 | it('should be able to inject ordering before plugins', () => { |
| 30 | const OnePlugin = createPlugin(); |
| 31 | const TwoPlugin = createPlugin(); |
| 32 | const ThreePlugin = createPlugin(); |
| 33 | |
| 34 | EventPluginRegistry.injectEventPluginOrder(['one', 'two', 'three']); |
| 35 | EventPluginRegistry.injectEventPluginsByName({ |
| 36 | one: OnePlugin, |
| 37 | two: TwoPlugin, |
| 38 | }); |
| 39 | EventPluginRegistry.injectEventPluginsByName({ |
| 40 | three: ThreePlugin, |
| 41 | }); |
| 42 | |
| 43 | expect(EventPluginRegistry.plugins.length).toBe(3); |
| 44 | expect(EventPluginRegistry.plugins[0]).toBe(OnePlugin); |
| 45 | expect(EventPluginRegistry.plugins[1]).toBe(TwoPlugin); |
| 46 | expect(EventPluginRegistry.plugins[2]).toBe(ThreePlugin); |
| 47 | }); |
| 48 | |
| 49 | it('should be able to inject plugins before and after ordering', () => { |
| 50 | const OnePlugin = createPlugin(); |
| 51 | const TwoPlugin = createPlugin(); |
| 52 | const ThreePlugin = createPlugin(); |
| 53 | |
| 54 | EventPluginRegistry.injectEventPluginsByName({ |
| 55 | one: OnePlugin, |
| 56 | two: TwoPlugin, |
| 57 | }); |
| 58 | EventPluginRegistry.injectEventPluginOrder(['one', 'two', 'three']); |
| 59 | EventPluginRegistry.injectEventPluginsByName({ |
| 60 | three: ThreePlugin, |
| 61 | }); |
| 62 | |
| 63 | expect(EventPluginRegistry.plugins.length).toBe(3); |
| 64 | expect(EventPluginRegistry.plugins[0]).toBe(OnePlugin); |
| 65 | expect(EventPluginRegistry.plugins[1]).toBe(TwoPlugin); |
| 66 | expect(EventPluginRegistry.plugins[2]).toBe(ThreePlugin); |
| 67 | }); |
| 68 | |
| 69 | it('should be able to inject repeated plugins and out-of-order', () => { |
| 70 | const OnePlugin = createPlugin(); |
| 71 | const TwoPlugin = createPlugin(); |
| 72 | const ThreePlugin = createPlugin(); |
| 73 | |
| 74 | EventPluginRegistry.injectEventPluginsByName({ |
| 75 | one: OnePlugin, |
| 76 | three: ThreePlugin, |
| 77 | }); |
| 78 | EventPluginRegistry.injectEventPluginOrder(['one', 'two', 'three']); |
| 79 | EventPluginRegistry.injectEventPluginsByName({ |
| 80 | two: TwoPlugin, |
| 81 | three: ThreePlugin, |
| 82 | }); |
| 83 | |
| 84 | expect(EventPluginRegistry.plugins.length).toBe(3); |
| 85 | expect(EventPluginRegistry.plugins[0]).toBe(OnePlugin); |
| 86 | expect(EventPluginRegistry.plugins[1]).toBe(TwoPlugin); |
| 87 | expect(EventPluginRegistry.plugins[2]).toBe(ThreePlugin); |
| 88 | }); |
| 89 | |
| 90 | it('should throw if plugin does not implement `extractEvents`', () => { |
| 91 | const BadPlugin = {}; |
| 92 | |
| 93 | EventPluginRegistry.injectEventPluginOrder(['bad']); |
| 94 | |
| 95 | expect(function () { |
| 96 | EventPluginRegistry.injectEventPluginsByName({ |
| 97 | bad: BadPlugin, |
| 98 | }); |
| 99 | }).toThrow( |
| 100 | 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' + |
| 101 | 'method, but `bad` does not.', |
| 102 | ); |
| 103 | }); |
| 104 | |
| 105 | it('should throw if plugin does not exist in ordering', () => { |
| 106 | const OnePlugin = createPlugin(); |
| 107 | const RandomPlugin = createPlugin(); |
| 108 | |
| 109 | EventPluginRegistry.injectEventPluginOrder(['one']); |
| 110 | |
| 111 | expect(function () { |
| 112 | EventPluginRegistry.injectEventPluginsByName({ |
| 113 | one: OnePlugin, |
| 114 | random: RandomPlugin, |
| 115 | }); |
| 116 | }).toThrow( |
| 117 | 'EventPluginRegistry: Cannot inject event plugins that do not exist ' + |
| 118 | 'in the plugin ordering, `random`.', |
| 119 | ); |
| 120 | }); |
| 121 | |
| 122 | it('should throw if ordering is injected more than once', () => { |
| 123 | const pluginOrdering = []; |
| 124 | |
| 125 | EventPluginRegistry.injectEventPluginOrder(pluginOrdering); |
| 126 | |
| 127 | expect(function () { |
| 128 | EventPluginRegistry.injectEventPluginOrder(pluginOrdering); |
| 129 | }).toThrow( |
| 130 | 'EventPluginRegistry: Cannot inject event plugin ordering more than ' + |
| 131 | 'once. You are likely trying to load more than one copy of React.', |
| 132 | ); |
| 133 | }); |
| 134 | |
| 135 | it('should throw if different plugins injected using same name', () => { |
| 136 | const OnePlugin = createPlugin(); |
| 137 | const TwoPlugin = createPlugin(); |
| 138 | |
| 139 | EventPluginRegistry.injectEventPluginsByName({same: OnePlugin}); |
| 140 | |
| 141 | expect(function () { |
| 142 | EventPluginRegistry.injectEventPluginsByName({same: TwoPlugin}); |
| 143 | }).toThrow( |
| 144 | 'EventPluginRegistry: Cannot inject two different event plugins using ' + |
| 145 | 'the same name, `same`.', |
| 146 | ); |
| 147 | }); |
| 148 | |
| 149 | it('should publish registration names of injected plugins', () => { |
| 150 | const OnePlugin = createPlugin({ |
| 151 | eventTypes: { |
| 152 | click: {registrationName: 'onClick'}, |
| 153 | focus: {registrationName: 'onFocus'}, |
| 154 | }, |
| 155 | }); |
| 156 | const TwoPlugin = createPlugin({ |
| 157 | eventTypes: { |
| 158 | magic: { |
| 159 | phasedRegistrationNames: { |
| 160 | bubbled: 'onMagicBubble', |
| 161 | captured: 'onMagicCapture', |
| 162 | }, |
| 163 | }, |
| 164 | }, |
| 165 | }); |
| 166 | |
| 167 | EventPluginRegistry.injectEventPluginsByName({one: OnePlugin}); |
| 168 | EventPluginRegistry.injectEventPluginOrder(['one', 'two']); |
| 169 | |
| 170 | expect( |
| 171 | Object.keys(EventPluginRegistry.registrationNameModules).length, |
| 172 | ).toBe(2); |
| 173 | expect(EventPluginRegistry.registrationNameModules.onClick).toBe(OnePlugin); |
| 174 | expect(EventPluginRegistry.registrationNameModules.onFocus).toBe(OnePlugin); |
| 175 | |
| 176 | EventPluginRegistry.injectEventPluginsByName({two: TwoPlugin}); |
| 177 | |
| 178 | expect( |
| 179 | Object.keys(EventPluginRegistry.registrationNameModules).length, |
| 180 | ).toBe(4); |
| 181 | expect(EventPluginRegistry.registrationNameModules.onMagicBubble).toBe( |
| 182 | TwoPlugin, |
| 183 | ); |
| 184 | expect(EventPluginRegistry.registrationNameModules.onMagicCapture).toBe( |
| 185 | TwoPlugin, |
| 186 | ); |
| 187 | }); |
| 188 | |
| 189 | it('should throw if multiple registration names collide', () => { |
| 190 | const OnePlugin = createPlugin({ |
| 191 | eventTypes: { |
| 192 | photoCapture: {registrationName: 'onPhotoCapture'}, |
| 193 | }, |
| 194 | }); |
| 195 | const TwoPlugin = createPlugin({ |
| 196 | eventTypes: { |
| 197 | photo: { |
| 198 | phasedRegistrationNames: { |
| 199 | bubbled: 'onPhotoBubble', |
| 200 | captured: 'onPhotoCapture', |
| 201 | }, |
| 202 | }, |
| 203 | }, |
| 204 | }); |
| 205 | |
| 206 | EventPluginRegistry.injectEventPluginsByName({ |
| 207 | one: OnePlugin, |
| 208 | two: TwoPlugin, |
| 209 | }); |
| 210 | |
| 211 | expect(function () { |
| 212 | EventPluginRegistry.injectEventPluginOrder(['one', 'two']); |
| 213 | }).toThrow( |
| 214 | 'EventPluginRegistry: More than one plugin attempted to publish the same ' + |
| 215 | 'registration name, `onPhotoCapture`.', |
| 216 | ); |
| 217 | }); |
| 218 | |
| 219 | it('should throw if an invalid event is published', () => { |
| 220 | const OnePlugin = createPlugin({ |
| 221 | eventTypes: { |
| 222 | badEvent: { |
| 223 | /* missing configuration */ |
| 224 | }, |
| 225 | }, |
| 226 | }); |
| 227 | |
| 228 | EventPluginRegistry.injectEventPluginsByName({one: OnePlugin}); |
| 229 | |
| 230 | expect(function () { |
| 231 | EventPluginRegistry.injectEventPluginOrder(['one']); |
| 232 | }).toThrow( |
| 233 | 'EventPluginRegistry: Failed to publish event `badEvent` for plugin ' + |
| 234 | '`one`.', |
| 235 | ); |
| 236 | }); |
| 237 | }); |