go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/testing_tools/jest_utils.ts (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import * as path from 'path';
    16  
    17  import callsites from 'callsites';
    18  
    19  /**
    20   * Resolves a module in the specified file.
    21   */
    22  function resolveModuleInFile(moduleName: string, filename: string): string {
    23    if (!moduleName.startsWith('.')) {
    24      return moduleName;
    25    }
    26  
    27    return path.join(path.dirname(filename), moduleName);
    28  }
    29  
    30  /**
    31   * See the type definition for `self.createSelectiveMockFromModule`.
    32   */
    33  export function createSelectiveMockFromModule<T = unknown>(
    34    moduleName: string,
    35    keysToMock: ReadonlyArray<keyof NoInfer<T>>,
    36  ): T {
    37    // Resolve the module at the caller site.
    38    // This allows the caller to mock a module with a relative path.
    39    moduleName = resolveModuleInFile(moduleName, callsites()[1].getFileName()!);
    40    const actualModule = jest.requireActual(moduleName);
    41    const mockedModule = jest.createMockFromModule(moduleName) as T;
    42  
    43    return {
    44      ...actualModule,
    45      ...Object.fromEntries(keysToMock.map((k) => [k, mockedModule[k]])),
    46    };
    47  }
    48  
    49  /**
    50   * See the type definition for `self.createSelectiveSpiesFromModule`.
    51   */
    52  export function createSelectiveSpiesFromModule<T = unknown>(
    53    moduleName: string,
    54    keysToSpy: ReadonlyArray<FunctionKeys<NoInfer<T>>>,
    55  ): T {
    56    // Resolve the module at the caller site.
    57    // This allows the caller to mock a module with a relative path.
    58    moduleName = resolveModuleInFile(moduleName, callsites()[1].getFileName()!);
    59    const actualModule = jest.requireActual(moduleName);
    60    return {
    61      ...actualModule,
    62      ...Object.fromEntries(keysToSpy.map((k) => [k, jest.fn(actualModule[k])])),
    63    };
    64  }