diff --git a/gulpfile.js b/gulpfile.js index 93dbf89..9b2c1d9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -23,10 +23,11 @@ const FILE_EXTENSION_XPI = 'xpi'; const BROWSER_TYPE_CHROME = 'chrome'; const BROWSER_TYPE_FIREFOX = 'firefox'; -let scriptFilePath = url.fileURLToPath(import.meta.url); -let scriptDirPath = path.dirname(scriptFilePath); -let srcDirPath = path.resolve(scriptDirPath, 'src'); -let distDirPath = path.resolve(scriptDirPath, 'dist'); +const scriptFilePath = url.fileURLToPath(import.meta.url); +const scriptDirPath = path.dirname(scriptFilePath); +const srcDirPath = path.resolve(scriptDirPath, 'src'); +const distDirPath = path.resolve(scriptDirPath, 'dist'); +const popupDistDirPath = path.resolve(distDirPath, 'popup'); async function buildCustomElements(outputDirPath) { await vite.build({ @@ -41,10 +42,7 @@ async function buildCustomElements(outputDirPath) { }, }, define: { - 'process.env': { - // TODO: Need to run this for each extension (chrome/firefox) - EXTENSION_ID: process.env.EXTENSION_ID, - }, + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), }, plugins: [vue({ customElement: true }), svgLoader()], }); @@ -76,6 +74,19 @@ async function buildCustomElements(outputDirPath) { } } +async function buildPopup(outputDirPath) { + const popupRoot = path.join(srcDirPath, 'popup'); + await vite.build({ + root: popupRoot, + base: '', + build: { + emptyOutDir: false, + outDir: outputDirPath, + }, + plugins: [vue()], + }); +} + async function buildBrowserExtension(browserType, version, fileExtension) { let outputDirPath = path.join(distDirPath, browserType); @@ -88,6 +99,10 @@ async function buildBrowserExtension(browserType, version, fileExtension) { await gulp.src(path.join(distDirPath, 'custom-elements.js')).pipe(gulp.dest(path.join(outputDirPath))); await gulp.src(path.join(distDirPath, 'custom-elements.css')).pipe(gulp.dest(path.join(outputDirPath))); + // -------------- + // popup + await gulp.src(path.join(popupDistDirPath, '**', '*')).pipe(gulp.dest(path.join(outputDirPath, 'popup'))); + // -------------- // manifest.json let manifestFilename = `manifest.${browserType}.json`; @@ -132,6 +147,7 @@ gulp.task('compile', async () => { console.log(`compiling version ${version}`); await buildCustomElements(distDirPath); + await buildPopup(popupDistDirPath); await buildBrowserExtension(BROWSER_TYPE_CHROME, version, FILE_EXTENSION_ZIP); await buildBrowserExtension(BROWSER_TYPE_FIREFOX, version, FILE_EXTENSION_XPI); }); diff --git a/package.json b/package.json index aec3780..340933f 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@rollup/plugin-commonjs": "^24.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@types/chrome": "^0.0.206", + "@types/firefox-webext-browser": "^111.0.1", "@vitejs/plugin-vue": "^3.2.0", "@webcomponents/custom-elements": "^1.5.1", "del": "^7.0.0", diff --git a/src/background/background-events.js b/src/background/background-events.js index d77cddb..cb385a6 100644 --- a/src/background/background-events.js +++ b/src/background/background-events.js @@ -1,3 +1,4 @@ +import browser from '../browser'; import { REQUEST_PACKAGE_INFO_EVENT, RESPONSE_PACKAGE_INFO_EVENT } from '../events-shared'; import advisories from './advisory/index'; @@ -22,7 +23,7 @@ const listener = async ({ type, detail }, port) => { }; export const listen = () => { - chrome.runtime.onConnect.addListener((port) => { + browser.runtime.onConnect.addListener((port) => { port.onMessage.addListener(listener); }); }; diff --git a/src/browser.js b/src/browser.js new file mode 100644 index 0000000..654be6e --- /dev/null +++ b/src/browser.js @@ -0,0 +1 @@ +export default globalThis.browser || globalThis.chrome; diff --git a/src/content/content-events.js b/src/content/content-events.js index bd453fe..af32315 100644 --- a/src/content/content-events.js +++ b/src/content/content-events.js @@ -1,21 +1,29 @@ +import browser from '../browser'; import { addMessagingEventListener, CONTENT_PORT_CONNECTION, dispatchEvent, + EVENT_SETTINGS_CHANGED, READY_EVENT, REQUEST_PACKAGE_INFO_EVENT, RESPONSE_PACKAGE_INFO_EVENT, } from '../events-shared'; +import * as storage from '../storage'; const sendPackageInfoToWebapp = (info) => dispatchEvent(RESPONSE_PACKAGE_INFO_EVENT, info); -const backgroundConnection = chrome.runtime.connect({ name: CONTENT_PORT_CONNECTION }); +const backgroundConnection = browser.runtime.connect({ name: CONTENT_PORT_CONNECTION }); backgroundConnection.onMessage.addListener((message) => { if (message.type === RESPONSE_PACKAGE_INFO_EVENT) { sendPackageInfoToWebapp(message.detail); } }); +export const sendEventSettingsChangedToWebapp = async () => { + const settings = await storage.getAllAdvisoriesSettings(); + dispatchEvent(EVENT_SETTINGS_CHANGED, settings); +}; + export const fetchPackageInfo = (packageId) => { backgroundConnection.postMessage({ type: REQUEST_PACKAGE_INFO_EVENT, detail: packageId }); }; @@ -43,4 +51,10 @@ export const listen = () => { console.log('Ready event received from injected script'); isWebappReady = true; }); + + browser.runtime.onMessage.addListener((message) => { + if (message.type === EVENT_SETTINGS_CHANGED) { + sendEventSettingsChangedToWebapp(); + } + }); }; diff --git a/src/content/content.js b/src/content/content.js index 149f82f..7e06335 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -1,14 +1,15 @@ +import browser from '../browser'; import * as events from './content-events'; const injectScriptTag = () => { const script = document.createElement('script'); - script.src = chrome.runtime.getURL('custom-elements.js'); + script.src = browser.runtime.getURL('custom-elements.js'); (document.head || document.documentElement).appendChild(script); console.log('Injected script tag', script); const link = document.createElement('link'); link.rel = 'stylesheet'; - link.href = chrome.runtime.getURL('custom-elements.css'); + link.href = browser.runtime.getURL('custom-elements.css'); (document.head || document.documentElement).appendChild(link); console.log('Injected link tag', link); }; @@ -27,6 +28,8 @@ export const mountContentScript = (contentScript) => { return; } + events.sendEventSettingsChangedToWebapp(); + await contentScript(); console.log('Overlay is finished'); diff --git a/src/custom-elements/Indicator.vue b/src/custom-elements/Indicator.vue index 0c69160..c1a769a 100644 --- a/src/custom-elements/Indicator.vue +++ b/src/custom-elements/Indicator.vue @@ -24,9 +24,9 @@ + + diff --git a/src/popup/index.html b/src/popup/index.html new file mode 100644 index 0000000..61dbcdc --- /dev/null +++ b/src/popup/index.html @@ -0,0 +1,14 @@ + + + + + + + Vite App + + + +
+ + + diff --git a/src/popup/main.js b/src/popup/main.js new file mode 100644 index 0000000..557f81a --- /dev/null +++ b/src/popup/main.js @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import Popup from './Popup.vue'; + +createApp(Popup).mount('#app'); diff --git a/src/popup/popup-events.js b/src/popup/popup-events.js new file mode 100644 index 0000000..38d02c4 --- /dev/null +++ b/src/popup/popup-events.js @@ -0,0 +1,5 @@ +import { EVENT_SETTINGS_CHANGED, sendMessageToAllTabs } from '../events-shared'; + +export const sendEventSettingsChanged = () => { + sendMessageToAllTabs(EVENT_SETTINGS_CHANGED); +}; diff --git a/src/storage.js b/src/storage.js new file mode 100644 index 0000000..f6f5653 --- /dev/null +++ b/src/storage.js @@ -0,0 +1,14 @@ +import browser from './browser'; + +const defaultAdvisoriesSettings = { + snyk: true, + socket: true, + debricked: true, + depsDev: true, + openbase: true, +}; + +export const getAllAdvisoriesSettings = () => + browser.storage.local.get({ advisories: defaultAdvisoriesSettings }).then(({ advisories }) => advisories); + +export const setAllAdvisoriesSettings = (advisories) => browser.storage.local.set({ advisories }); diff --git a/tests/README.md b/tests/README.md index 7e3c5a0..610c859 100644 --- a/tests/README.md +++ b/tests/README.md @@ -5,3 +5,28 @@ These tests are _Integration Tests_ that are used to check our code against real To avoid _Too Many Requests_ block, we are saving the webpages snapshot by running the `yarn update-webpages-snapshot` command manually. Then, we are running our code against the local snapshots. + +# Manual Test Script + +## What we testing + +- The extension is able to detect the packages in the page +- The page contacting with the background script +- The background script is able to fetch the advisories from the API +- The popup is working correctly +- Changes in the popup affect the indicator immediately + +## Prerequisites + +- Install the extension on _Chrome_ and _Firefox_ browsers +- Have a test page ready (e.g. https://stackoverflow.com/questions/33527653) + +## Steps + +1. Open the test page on both browsers +2. Verify that the indicator appears on the packages in the page and shows the number of issues +3. Click on the extension logo to open his popup +4. Verify that the popup shows the details of the advisories and has a toggle button to enable or disable them +5. Click on the toggle button to disable the advisories +6. Verify that the indicator updates and shows zero issues +7. Refresh the page and verify that the indicator is still zero diff --git a/yarn.lock b/yarn.lock index a9a2c4c..7e57250 100644 --- a/yarn.lock +++ b/yarn.lock @@ -798,6 +798,11 @@ resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee" integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ== +"@types/firefox-webext-browser@^111.0.1": + version "111.0.1" + resolved "https://registry.yarnpkg.com/@types/firefox-webext-browser/-/firefox-webext-browser-111.0.1.tgz#1ab4eacbe8eab48bb8c450c44519bac08a041a70" + integrity sha512-mmHWdQTCT68X0hh0URrsIyWhJeFzZHaiprj6nni/CmsAmqYq27T0eZyu1ePeKJ/zuDD3wqtTzm5TwRFAso+oPw== + "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae"