diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/useOpenKey.test.ts b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/useOpenKey.test.ts index ab7da015d..93a581603 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/useOpenKey.test.ts +++ b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/useOpenKey.test.ts @@ -1,7 +1,7 @@ import type { KeyboardEvent } from "react"; import { renderHook } from "@testing-library/react"; import type { FloatingContext } from "@floating-ui/react"; -import { mockedObject } from "../../../../../test/vscode-tests/utils/mocking.helpers"; +import { mockedObject } from "../../../../../test/mocked-object"; import { useOpenKey } from "../useOpenKey"; describe("useOpenKey", () => { diff --git a/extensions/ql-vscode/test/mocked-object.ts b/extensions/ql-vscode/test/mocked-object.ts new file mode 100644 index 000000000..e20caba29 --- /dev/null +++ b/extensions/ql-vscode/test/mocked-object.ts @@ -0,0 +1,80 @@ +export type DeepPartial = T extends object + ? { + [P in keyof T]?: DeepPartial; + } + : T; + +type DynamicProperties = { + [P in keyof T]?: () => T[P]; +}; + +type MockedObjectOptions = { + /** + * Properties for which the given method should be called when accessed. + * The method should return the value to be returned when the property is accessed. + * Methods which are explicitly defined in `methods` will take precedence over + * dynamic properties. + */ + dynamicProperties?: DynamicProperties; +}; + +export function mockedObject( + props: DeepPartial, + { dynamicProperties }: MockedObjectOptions = {}, +): T { + return new Proxy({} as unknown as T, { + get: (_target, prop) => { + if (prop in props) { + return (props as any)[prop]; + } + if (dynamicProperties && prop in dynamicProperties) { + return (dynamicProperties as any)[prop](); + } + + // The `then` method is accessed by `Promise.resolve` to check if the object is a thenable. + // We don't want to throw an error when this happens. + if (prop === "then") { + return undefined; + } + + // The `asymmetricMatch` is accessed by jest to check if the object is a matcher. + // We don't want to throw an error when this happens. + if (prop === "asymmetricMatch") { + return undefined; + } + + // The `Symbol.iterator` is accessed by jest to check if the object is iterable. + // We don't want to throw an error when this happens. + if (prop === Symbol.iterator) { + return undefined; + } + + // The `$$typeof` is accessed by jest to check if the object is a React element. + // We don't want to throw an error when this happens. + if (prop === "$$typeof") { + return undefined; + } + + // The `nodeType` and `tagName` are accessed by jest to check if the object is a DOM node. + // We don't want to throw an error when this happens. + if (prop === "nodeType" || prop === "tagName") { + return undefined; + } + + // The `@@__IMMUTABLE_ITERABLE__@@` and variants are accessed by jest to check if the object is an + // immutable object (from Immutable.js). + // We don't want to throw an error when this happens. + if (prop.toString().startsWith("@@__IMMUTABLE_")) { + return undefined; + } + + // The `Symbol.toStringTag` is accessed by jest. + // We don't want to throw an error when this happens. + if (prop === Symbol.toStringTag) { + return "MockedObject"; + } + + throw new Error(`Method ${String(prop)} not mocked`); + }, + }); +} diff --git a/extensions/ql-vscode/test/vscode-tests/utils/mocking.helpers.ts b/extensions/ql-vscode/test/vscode-tests/utils/mocking.helpers.ts index b09e8dac5..8004ef2ca 100644 --- a/extensions/ql-vscode/test/vscode-tests/utils/mocking.helpers.ts +++ b/extensions/ql-vscode/test/vscode-tests/utils/mocking.helpers.ts @@ -2,86 +2,11 @@ import type { QuickPickItem, window, Uri } from "vscode"; import type { DatabaseItem } from "../../../src/databases/local-databases"; import type { Octokit } from "@octokit/rest"; -export type DeepPartial = T extends object - ? { - [P in keyof T]?: DeepPartial; - } - : T; +import type { DeepPartial } from "../../mocked-object"; +import { mockedObject } from "../../mocked-object"; -type DynamicProperties = { - [P in keyof T]?: () => T[P]; -}; - -type MockedObjectOptions = { - /** - * Properties for which the given method should be called when accessed. - * The method should return the value to be returned when the property is accessed. - * Methods which are explicitly defined in `methods` will take precedence over - * dynamic properties. - */ - dynamicProperties?: DynamicProperties; -}; - -export function mockedObject( - props: DeepPartial, - { dynamicProperties }: MockedObjectOptions = {}, -): T { - return new Proxy({} as unknown as T, { - get: (_target, prop) => { - if (prop in props) { - return (props as any)[prop]; - } - if (dynamicProperties && prop in dynamicProperties) { - return (dynamicProperties as any)[prop](); - } - - // The `then` method is accessed by `Promise.resolve` to check if the object is a thenable. - // We don't want to throw an error when this happens. - if (prop === "then") { - return undefined; - } - - // The `asymmetricMatch` is accessed by jest to check if the object is a matcher. - // We don't want to throw an error when this happens. - if (prop === "asymmetricMatch") { - return undefined; - } - - // The `Symbol.iterator` is accessed by jest to check if the object is iterable. - // We don't want to throw an error when this happens. - if (prop === Symbol.iterator) { - return undefined; - } - - // The `$$typeof` is accessed by jest to check if the object is a React element. - // We don't want to throw an error when this happens. - if (prop === "$$typeof") { - return undefined; - } - - // The `nodeType` and `tagName` are accessed by jest to check if the object is a DOM node. - // We don't want to throw an error when this happens. - if (prop === "nodeType" || prop === "tagName") { - return undefined; - } - - // The `@@__IMMUTABLE_ITERABLE__@@` and variants are accessed by jest to check if the object is an - // immutable object (from Immutable.js). - // We don't want to throw an error when this happens. - if (prop.toString().startsWith("@@__IMMUTABLE_")) { - return undefined; - } - - // The `Symbol.toStringTag` is accessed by jest. - // We don't want to throw an error when this happens. - if (prop === Symbol.toStringTag) { - return "MockedObject"; - } - - throw new Error(`Method ${String(prop)} not mocked`); - }, - }); -} +export { mockedObject }; +export type { DeepPartial }; export function mockedOctokitFunction< Namespace extends keyof Octokit["rest"],