diff --git a/src/cjs/types.ts b/src/cjs/types.ts index 670936e3e..d64190b0b 100644 --- a/src/cjs/types.ts +++ b/src/cjs/types.ts @@ -175,7 +175,7 @@ export type AppBuildInfo = { }; export type ExecuteOpts = { - internal: boolean; + internal?: boolean; }; export type WdioElectronWindowObj = { @@ -192,10 +192,12 @@ type Override = | 'mockRejectedValue' | 'mockRejectedValueOnce' | 'mockClear' - | 'mockReset'; -type NotImplemented = 'withImplementation'; + | 'mockReset' + | 'mockReturnThis' + | 'mockName' + | 'withImplementation'; -interface ElectronMockInstance extends Omit { +interface ElectronMockInstance extends Omit { /** * Accepts a function that will be used as an implementation of the mock. * @@ -218,7 +220,7 @@ interface ElectronMockInstance extends Omit { */ mockImplementation(fn: AbstractFn): Promise; /** - * Accepts a function that will be used as mock's implementation during the next call. If chained, every consecutive call will produce different results. + * Accepts a function that will be used as the mock's implementation during the next call. If chained, every consecutive call will produce different results. * * When the mocked function runs out of implementations, it will invoke the default implementation set with `mockImplementation`. * @@ -411,6 +413,100 @@ interface ElectronMockInstance extends Omit { * ``` */ mockRestore(): Promise; + /** + * Useful if you need to return the `this` context from the method without invoking implementation. This is a shorthand for: + * + * ```js + * await spy.mockImplementation(function () { + * return this; + * }); + * ``` + * + * ...which enables API functions to be chained: + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * const mockGetVersion = await browser.electron.mock('app', 'getVersion'); + * await mockGetName.mockReturnThis(); + * await browser.electron.execute((electron) => + * electron.app.getName().getVersion() + * ); + * + * expect(mockGetVersion).toHaveBeenCalled(); + * ``` + */ + mockReturnThis(): Promise; + /** + * Overrides the original mock implementation temporarily while the callback is being executed. + * The electron object is passed into the callback in the same way as for `execute`. + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * const withImplementationResult = await mockGetName.withImplementation( + * () => 'temporary mock name', + * (electron) => electron.app.getName(), + * ); + * + * expect(withImplementationResult).toBe('temporary mock name'); + * ``` + * + * It can also be used with an asynchronous callback: + * + * @example + * ```js + * const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon'); + * const withImplementationResult = await mockGetFileIcon.withImplementation( + * () => Promise.resolve('temporary mock icon'), + * async (electron) => await electron.app.getFileIcon('/path/to/icon'), + * ); + * + * expect(withImplementationResult).toBe('temporary mock icon'); + * ``` + * + */ + withImplementation( + implFn: AbstractFn, + callbackFn: (electron: typeof Electron, ...innerArgs: InnerArguments) => ReturnValue, + ): Promise; + /** + * Assigns a name to the mock. Useful to see the name of the mock if an assertion fails. + * The name can be retrieved via `getMockName`. + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * mockGetName.mockName('test mock'); + * + * expect(mockGetName.getMockName()).toBe('test mock'); + * ``` + */ + mockName(name: string): ElectronMock; + /** + * Returns the assigned name of the mock. Defaults to `electron..`. + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * + * expect(mockGetName.getMockName()).toBe('electron.app.getName'); + * ``` + */ + getMockName(): string; + /** + * Returns the current mock implementation. The default implementation is an empty function (returns `undefined`). + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * await mockGetName.mockImplementation(() => 'mocked name'); + * const mockImpl = mockGetName.getMockImplementation(); + * + * expect(mockImpl()).toBe('mocked name'); + * ``` + */ + getMockImplementation(): AbstractFn; /** * Used internally to update the outer mock function with calls from the inner (Electron context) mock. */ diff --git a/src/types.ts b/src/types.ts index 1bee24903..d64190b0b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -182,7 +182,7 @@ export type WdioElectronWindowObj = { execute: (script: string, args?: unknown[]) => unknown; }; -type AsyncOverride = +type Override = | 'mockImplementation' | 'mockImplementationOnce' | 'mockReturnValue' @@ -194,9 +194,10 @@ type AsyncOverride = | 'mockClear' | 'mockReset' | 'mockReturnThis' + | 'mockName' | 'withImplementation'; -interface ElectronMockInstance extends Omit { +interface ElectronMockInstance extends Omit { /** * Accepts a function that will be used as an implementation of the mock. * @@ -219,7 +220,7 @@ interface ElectronMockInstance extends Omit { */ mockImplementation(fn: AbstractFn): Promise; /** - * Accepts a function that will be used as mock's implementation during the next call. If chained, every consecutive call will produce different results. + * Accepts a function that will be used as the mock's implementation during the next call. If chained, every consecutive call will produce different results. * * When the mocked function runs out of implementations, it will invoke the default implementation set with `mockImplementation`. * @@ -413,7 +414,7 @@ interface ElectronMockInstance extends Omit { */ mockRestore(): Promise; /** - * Useful if you need to return `this` context from the method without invoking implementation. This is a shorthand for: + * Useful if you need to return the `this` context from the method without invoking implementation. This is a shorthand for: * * ```js * await spy.mockImplementation(function () { @@ -421,6 +422,8 @@ interface ElectronMockInstance extends Omit { * }); * ``` * + * ...which enables API functions to be chained: + * * @example * ```js * const mockGetName = await browser.electron.mock('app', 'getName'); @@ -449,7 +452,7 @@ interface ElectronMockInstance extends Omit { * expect(withImplementationResult).toBe('temporary mock name'); * ``` * - * Can also be used with an asynchronous callback: + * It can also be used with an asynchronous callback: * * @example * ```js @@ -467,6 +470,43 @@ interface ElectronMockInstance extends Omit { implFn: AbstractFn, callbackFn: (electron: typeof Electron, ...innerArgs: InnerArguments) => ReturnValue, ): Promise; + /** + * Assigns a name to the mock. Useful to see the name of the mock if an assertion fails. + * The name can be retrieved via `getMockName`. + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * mockGetName.mockName('test mock'); + * + * expect(mockGetName.getMockName()).toBe('test mock'); + * ``` + */ + mockName(name: string): ElectronMock; + /** + * Returns the assigned name of the mock. Defaults to `electron..`. + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * + * expect(mockGetName.getMockName()).toBe('electron.app.getName'); + * ``` + */ + getMockName(): string; + /** + * Returns the current mock implementation. The default implementation is an empty function (returns `undefined`). + * + * @example + * ```js + * const mockGetName = await browser.electron.mock('app', 'getName'); + * await mockGetName.mockImplementation(() => 'mocked name'); + * const mockImpl = mockGetName.getMockImplementation(); + * + * expect(mockImpl()).toBe('mocked name'); + * ``` + */ + getMockImplementation(): AbstractFn; /** * Used internally to update the outer mock function with calls from the inner (Electron context) mock. */