-
-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for chrome.action
#11
Comments
I'm in the process of updating my projects to MV3, I'll put this on the todo list! Thanks for mentioning it, @Mrtenz 🥇 |
Hey @jacksteamdev - first of all, thank you for this awesome project. I was about to ask if you're planning on adding Manifest V3 support. For those who are stuck right now and would like to mock Here is the helper: // ./test-helper.ts
type Join<K, P> = K extends string | number
? P extends string | number
? `${K}${'' extends P ? '' : '.'}${P}`
: never
: never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]];
type Paths<T, D extends number = 10> = [D] extends [never]
? never
: T extends object
? {
[K in keyof T]-?: K extends string | number
? `${K}` | Join<K, Paths<T[K], Prev[D]>>
: never;
}[keyof T]
: '';
/* Utility function to mock currently unavailable methods in
'jest-chrome */
/**
* Takes a path to a method of the Chrome API. Properties are accessed
* via dot notation. Example:
* ```
* const scriptMock = mockForV3('scripting.executeScript')
* ```
* This will produce
* ```
* global.chrome.scripting.executeScript = jest.fn()
* ```
* The returned mock function above will mock
* `scripting.executeScript`. Each returned mock function has all the
* Jest methods available and you can add your custom implementations
* as usual.
* ```
* scriptMock.mockImplementation(() => true)
* ```
* @param args string
* @returns jest.Mock - Generic jest mock function
*
*/
export default function <T extends Paths<typeof chrome>>(path: T) {
const mockFn = jest.fn();
const keys = path.split('.');
function deepRecreate(): void {
const methods = keys.reduceRight((obj, next, idx) => {
if (idx === keys.length - 1) {
return { [next]: mockFn };
}
return { [next]: obj };
}, {});
Object.assign(global.chrome, methods);
}
deepRecreate();
return mockFn;
} If you want to go the easy way, just put it somewhere and import the module when necessary. The function will recreate the global chrome object according to the path you provide as a string. With Typescript, you even get IntelliSense and linting. The function will return a new mock function with all the Jest methods available, and you can add your custom mock implementation. If you want to have type-safe mocks, just cast the returned mock to be whatever you want (as in the official docs). Otherwise, just use the returned mock, but be aware that, depending on what you test, your tests may fail because you're not providing the correct implementations. Here is an example with a "loosely" typed mock (a mock implementation that does not conform to the actual API and a "strongly" typed mock: // ./test/example.spec.ts
import mockForV3 from '../test-helper';
// Mocking a V3 API
async function getTabGroup() {
const { color } = await chrome.tabGroups.get(1);
if (!color) throw new Error('No color!');
return color;
}
describe('Sample test', () => {
it('fails with loose implementation', async () => {
// Example with "loose implementation"
const looseScriptMock = mockForV3('tabGroups.get');
// Custom response, does not have to follow API interface
looseScriptMock.mockImplementation(async () => ({
collapsed: true,
// color: 'blue', // Left out!
id: 1,
windowId: 1
}));
await expect(getTabGroup()).rejects.toThrow();
});
it('works with strong implementation', async () => {
// Example with mocking the actual implementation
const strongScriptMock = mockForV3('tabGroups.get') as jest.MockedFunction<
typeof chrome.tabGroups.get
>;
// TS complains if something is removed from the mocked response
strongScriptMock.mockImplementation(async () => ({
collapsed: true,
color: 'blue',
id: 1,
windowId: 1
}));
await expect(getTabGroup()).resolves.toBe('blue');
});
});
BonusIf you want to have the helper available globally (just like // ./jest.setup.ts
// Assuming the helper file is in the root dir
global.mockForV3 = require('./test-helper').default;
// From the package
Object.assign(global, require('jest-chrome')); Create a global.d.ts file (or any other name) and add this: // ./typings/global.d.ts
declare var mockForV3: typeof import('../test-helper').default; Note that in this case, the And, last but not least, make sure TS is aware of your definitions file. {
"compilerOptions": {
...
"include": ["src/**/*", "test/**/*", "typings/**/*", "jest.setup.ts"],
}
Now To recap: This is my directory structure:
Maybe this is helpful to someone. Thank you for your work! |
@eegli That's some real TypeScript magic! Love it! Thank you so much. |
I ended up mocking
|
Is your feature request related to a problem? Please describe.
When using Manifest V3,
chrome.browserAction
(andpageAction
) is replaced withchrome.action
. It looks like this is not supported byjest-chrome
currently.Describe the solution you'd like
Support for
chrome.action
as alternative tochrome.browserAction
.The text was updated successfully, but these errors were encountered: