{"_id":"vitest-chrome","name":"vitest-chrome","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"vitest-chrome","version":"0.1.0","description":"Test Chrome extensions with Vitest. A complete mock of the Chrome API.","keywords":["api","chrome","chromium","extension","vitest","mock","test","typescript"],"repository":{"type":"git","url":"git+https://github.com/probil/vitest-chrome.git"},"bugs":{"url":"https://github.com/probil/vitest-chrome/issues"},"license":"MIT","author":{"name":"Max Liashuk","email":"m_lyashuk@ukr.net"},"main":"lib/index.cjs.js","module":"lib/index.esm.js","types":"types/index.d.ts","scripts":{"build":"run-s build:clean build:pro build:types build:copy","build:clean":"rm -rf lib types","build:copy":"copyfiles -f src/vitest-chrome.d.ts types","build:dev":"rollup -c --environment NODE_ENV:development","build:pro":"rollup -c --environment NODE_ENV:production","build:types":"tsc -p tsconfig.d.json","build:watch":"npm run build:dev -- -w","postpublish":"rm -rf node_modules package-lock.json && pnpm i","start":"run-p build:watch","pretest":"npm run build","test":"vitest"},"dependencies":{"@types/chrome":"^0.0.114"},"devDependencies":{"@rollup/plugin-commonjs":"^12.0.0","@rollup/plugin-json":"^4.0.3","@rollup/plugin-node-resolve":"^8.0.0","@rollup/plugin-typescript":"^4.1.2","@types/filesystem":"^0.0.29","@types/fs-extra":"^9.0.1","@types/lodash":"^4.14.153","@types/node":"^14.0.5","@types/power-assert":"^1.5.3","@types/puppeteer":"^3.0.0","@typescript-eslint/eslint-plugin":"^3.0.2","@typescript-eslint/parser":"^3.0.2","chrome-promise":"^3.0.5","copyfiles":"^2.3.0","eslint":"^7.1.0","eslint-plugin-jest":"^23.13.2","fs-extra":"^9.0.0","jsdom":"^22.1.0","lodash":"^4.17.15","npm-run-all":"^4.1.5","prettier":"^2.0.5","rollup":"^2.11.2","tslib":"^2.0.0","typescript":"^5.1.6","vitest":"^0.34.2"},"gitHead":"3004516bf37221632a8d77c262fc33c4a294a0bc","homepage":"https://github.com/probil/vitest-chrome#readme","_id":"vitest-chrome@0.1.0","_nodeVersion":"18.14.0","_npmVersion":"9.3.1","dist":{"integrity":"sha512-Bs1uywAolbc46h2tcaETQyFMTnOHjSA85iH10Zoe8ZMEl/71nuSdxs3z+KMxVZtN88TEgWicnl5vmbDO1OhoEw==","shasum":"427193099055dca5b403c9fe2cf5c46b0fc2ae3e","tarball":"https://registry.npmjs.org/vitest-chrome/-/vitest-chrome-0.1.0.tgz","fileCount":12,"unpackedSize":693534,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB9OCXypS9K133c6Tj/sB0+3vkf7LNMf0Q6yw26gYb8aAiEA1zcIShmiDrrCUBjmQ7AjTNxsvtaUTv+qyHyivLQ5Dew="}]},"_npmUser":{"name":"probil","email":"m_lyashuk@ukr.net"},"directories":{},"maintainers":[{"name":"probil","email":"m_lyashuk@ukr.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/vitest-chrome_0.1.0_1692934522083_0.7777769953922216"},"_hasShrinkwrap":false}},"time":{"created":"2023-08-25T03:35:22.082Z","0.1.0":"2023-08-25T03:35:22.270Z","modified":"2023-08-25T03:35:22.567Z"},"maintainers":[{"name":"probil","email":"m_lyashuk@ukr.net"}],"description":"Test Chrome extensions with Vitest. A complete mock of the Chrome API.","homepage":"https://github.com/probil/vitest-chrome#readme","keywords":["api","chrome","chromium","extension","vitest","mock","test","typescript"],"repository":{"type":"git","url":"git+https://github.com/probil/vitest-chrome.git"},"author":{"name":"Max Liashuk","email":"m_lyashuk@ukr.net"},"bugs":{"url":"https://github.com/probil/vitest-chrome/issues"},"license":"MIT","readme":"[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua)\n\n---\n\n\n# `vitest-chrome`\n\nA complete mock of the Chrome API for Chrome extensions, for use\nwith Vitest.\n\nTypeScript support is built in. Each function and event is based\non the\n[`@types/chrome`](https://www.npmjs.com/package/@types/chrome)\npackage.\n\n## Installation\n\n```sh\nnpm i vitest-chrome -D\n```\n\nSet `chrome` in the global scope during setup so that it is\nmocked in imported modules. Add a setup file to `vite.config.js`:\n\n```javascript\n// vitest.config.js\n\nexport default defineConfig({\n  test: {\n    // Add this line to your Vitest config (if you don't have it yet)\n    setupFiles: './vitest.init.ts',\n  }\n})\n```\n\nUse the setup file to assign the mocked `chrome` object to the\n`global` object:\n\n```javascript\n// vitest.init.js\nimport * as chrome from '../src/index'\n\n// Add chrome object to global scope\nObject.assign(global, chrome)\n```\n\nImport `chrome` from `vitest-chrome` for Intellisense and linting.\nThis is the same object as `chrome` in the global scope.\n\n```javascript\nimport { chrome } from 'vitest-chrome'\n```\n\n## Usage\n\n> All of the following code blocks come from\n> [`tests/demo.test.ts`](tests/demo.test.ts).\n\n### Events\n\nEach mocked Event has all the normal methods (`addListener`,\n`hasListener`, `hasListeners`, and `removeListener`) plus two\nmore: `callListeners` and `clearListeners`.\n\n`callListeners` triggers a specific Event. Call `callListeners`\nwith the arguments you expect Chrome to pass to your event\nlisteners. Each event listener for that Event will be called with\nthose arguments.\n\n`clearListeners` removes all listeners for a specific Event.\n\n```javascript\ntest('chrome api events', () => {\n  const listenerSpy = vi.fn()\n  const sendResponseSpy = vi.fn()\n\n  chrome.runtime.onMessage.addListener(listenerSpy)\n\n  expect(listenerSpy).not.toBeCalled()\n  expect(chrome.runtime.onMessage.hasListeners()).toBe(true)\n\n  chrome.runtime.onMessage.callListeners(\n    { greeting: 'hello' }, // message\n    {}, // MessageSender object\n    sendResponseSpy, // SendResponse function\n  )\n\n  expect(listenerSpy).toBeCalledWith(\n    { greeting: 'hello' },\n    {},\n    sendResponseSpy,\n  )\n  expect(sendResponseSpy).not.toBeCalled()\n})\n```\n\n### Synchronous functions\n\nSome Chrome API functions are synchronous. Use these like any\nmocked function:\n\n```javascript\ntest('chrome api functions', () => {\n  const manifest = {\n    name: 'my chrome extension',\n    manifest_version: 2,\n    version: '1.0.0',\n  }\n\n  chrome.runtime.getManifest.mockImplementation(() => manifest)\n\n  expect(chrome.runtime.getManifest()).toEqual(manifest)\n  expect(chrome.runtime.getManifest).toBeCalled()\n})\n```\n\n### Functions with callbacks\n\nMost Chrome API functions do something asynchronous. They use\ncallbacks to handle the result. The mock implementation should be\nset to handle the callback.\n\n> Mocked functions have no default mock implementation!\n\n```javascript\ntest('chrome api functions with callback', () => {\n  const message = { greeting: 'hello?' }\n  const response = { greeting: 'here I am' }\n  const callbackSpy = vi.fn()\n\n  chrome.runtime.sendMessage.mockImplementation(\n    (message, callback) => {\n      callback(response)\n    },\n  )\n\n  chrome.runtime.sendMessage(message, callbackSpy)\n\n  expect(chrome.runtime.sendMessage).toBeCalledWith(\n    message,\n    callbackSpy,\n  )\n  expect(callbackSpy).toBeCalledWith(response)\n})\n```\n\n### Callbacks and `chrome.runtime.lastError`\n\nWhen something goes wrong in a callback, Chrome sets\n`chrome.runtime.lastError` to an object with a message property.\nIf you need to test this, set and clear `lastError` in the mock\nimplementation.\n\n> Remember that `lastError` is always undefined outside of a\n> callback!\n\n`lastError` is an object with a\n[getter function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)\nfor the `message` property. If `message` is not checked, Chrome\nwill log the error to the console. To emulate this, simply set\n`lastError` to an object with a getter that wraps a mock, as seen\nbelow:\n\n```javascript\ntest('chrome api functions with lastError', () => {\n  const message = { greeting: 'hello?' }\n  const response = { greeting: 'here I am' }\n\n  // lastError setup\n  const lastErrorMessage = 'this is an error'\n  const lastErrorGetter = vi.fn(() => lastErrorMessage)\n  const lastError = {\n    get message() {\n      return lastErrorGetter()\n    },\n  }\n\n  // mock implementation\n  chrome.runtime.sendMessage.mockImplementation(\n    (message, callback) => {\n      chrome.runtime.lastError = lastError\n\n      callback(response)\n\n      // lastError is undefined outside of a callback\n      delete chrome.runtime.lastError\n    },\n  )\n\n  // callback implementation\n  const lastErrorSpy = vi.fn()\n  const callbackSpy = vi.fn(() => {\n    if (chrome.runtime.lastError) {\n      lastErrorSpy(chrome.runtime.lastError.message)\n    }\n  })\n\n  // send a message\n  chrome.runtime.sendMessage(message, callbackSpy)\n\n  expect(callbackSpy).toBeCalledWith(response)\n  expect(lastErrorGetter).toBeCalled()\n  expect(lastErrorSpy).toBeCalledWith(lastErrorMessage)\n\n  // lastError has been cleared\n  expect(chrome.runtime.lastError).toBeUndefined()\n})\n```\n\n### Contributions\n\nThe `chrome` object is based on schemas from the Chromium\nproject, with thanks to\n[`sinon-chrome`](https://github.com/acvetkov/sinon-chrome) for\ncompiling the schemas.\n\nSpecial thanks to [@jacksteamdev](https://github.com/jacksteamdev)\nthe author of [`jest-chrome`](https://github.com/extend-chrome/jest-chrome) (which this project is based on)\n","readmeFilename":"README.md"}