go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/testing_tools/setup_after_env.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 crypto from 'crypto'; 16 import { TextDecoder, TextEncoder } from 'util'; 17 18 import '@testing-library/jest-dom'; 19 import 'isomorphic-fetch'; 20 import 'intersection-observer'; 21 import { matchers as emotionMatchers } from '@emotion/jest'; 22 import * as dotenv from 'dotenv'; 23 import * as idbKeyVal from 'idb-keyval'; 24 import { configure } from 'mobx'; 25 26 import { assertNonNullable } from '@/generic_libs/tools/utils'; 27 import '@/proto_utils/duration_patch'; 28 29 import { 30 createSelectiveMockFromModule, 31 createSelectiveSpiesFromModule, 32 } from './jest_utils'; 33 34 expect.extend(emotionMatchers); 35 36 // TODO(crbug/1347294): encloses all state modifying actions in mobx actions 37 // then delete this. 38 configure({ enforceActions: 'never' }); 39 40 dotenv.config({ 41 path: './.env.development', 42 }); 43 44 // Those variables are declared as `const` so their value won't be accidentally 45 // changed. But they are actually injected by `/configs.js` to `self` in 46 // production environment. Here we need to do the same so they are available to 47 // code run under the test environment. 48 const configJSVars = self as unknown as { 49 VERSION: typeof VERSION; 50 SETTINGS: typeof SETTINGS; 51 }; 52 configJSVars.VERSION = assertNonNullable(process.env['VITE_MILO_VERSION']); 53 configJSVars.SETTINGS = Object.freeze({ 54 buildbucket: { 55 host: assertNonNullable(process.env['VITE_BUILDBUCKET_HOST']), 56 }, 57 swarming: { 58 defaultHost: assertNonNullable(process.env['VITE_SWARMING_DEFAULT_HOST']), 59 }, 60 resultdb: { 61 host: assertNonNullable(process.env['VITE_RESULT_DB_HOST']), 62 }, 63 luciAnalysis: { 64 host: assertNonNullable(process.env['VITE_LUCI_ANALYSIS_HOST']), 65 }, 66 luciBisection: { 67 host: assertNonNullable(process.env['VITE_LUCI_BISECTION_HOST']), 68 }, 69 sheriffOMatic: { 70 host: assertNonNullable(process.env['VITE_SHERIFF_O_MATIC_HOST']), 71 }, 72 luciTreeStatus: { 73 host: assertNonNullable(process.env['VITE_TREE_STATUS_HOST']), 74 }, 75 luciNotify: { 76 host: assertNonNullable(process.env['VITE_LUCI_NOTIFY_HOST']), 77 }, 78 }); 79 80 // `jest.mock` calls are automatically moved to the beginning of a test file by 81 // the jest test runner (i.e. before any import statements), making it 82 // impossible to use imported symbols in the module factory. 83 // 84 // Make the following functions accessible through `self` so they can be used in 85 // the module factory in a 86 // `jest.mock('module-name', () => { /* module factory */ })` call. 87 self.createSelectiveMockFromModule = createSelectiveMockFromModule; 88 self.createSelectiveSpiesFromModule = createSelectiveSpiesFromModule; 89 90 self.TextEncoder = TextEncoder; 91 self.TextDecoder = TextDecoder as typeof self.TextDecoder; 92 93 self.CSSStyleSheet.prototype.replace = () => Promise.race([]); 94 95 jest.mock('idb-keyval'); 96 const idbMockStore = new Map(); 97 jest.mocked(idbKeyVal.get).mockImplementation(async (k) => { 98 return idbMockStore.get(k); 99 }); 100 jest.mocked(idbKeyVal.set).mockImplementation(async (k, v) => { 101 idbMockStore.set(k, v); 102 }); 103 104 Object.defineProperty(self, 'crypto', { 105 value: { 106 subtle: crypto.webcrypto.subtle, 107 // GetRandomValues is required by the nanoid package to run tests. 108 getRandomValues: (arr: unknown[]) => crypto.randomBytes(arr.length), 109 }, 110 }); 111 112 // jsdom does not support `scrollIntoView`. 113 // See https://github.com/jsdom/jsdom/issues/1695 114 window.HTMLElement.prototype.scrollIntoView = jest.fn(); 115 116 jest.mock('lit/decorators.js', () => ({ 117 ...jest.requireActual('lit/decorators.js'), 118 customElement(name: string) { 119 return function (eleCon: CustomElementConstructor) { 120 // jest's module mocking may cause the module to be initialized multiple 121 // times and causing the element to be registered multiple times, leading 122 // to error: 'NotSupportedError: This name has already been registered in 123 // the registry.' 124 // 125 // Register the element conditionally to avoid the error. 126 if (!customElements.get(name)) { 127 customElements.define(name, eleCon); 128 } 129 }; 130 }, 131 }));