From 013904acfdd26524ec3876ecdec320371f91f967 Mon Sep 17 00:00:00 2001 From: Brendan Early Date: Fri, 15 Mar 2024 18:27:18 -0500 Subject: [PATCH] fix the tests, remove code coverage mv3 makes code cov w/ istanbul virtually impossible due to restrictions on unsafe-eval --- .github/workflows/main.yml | 17 +- manifests/manifest-chrome-testing.json | 6 +- package.json | 1 - scripts/test-runner.ts | 18 -- src/browser.ts | 11 +- src/components/Popup/MenuPage.vue | 2 +- src/test.ts | 6 + .../Popup/EnterPasswordPage.test.ts | 178 +++++----- src/test/components/Popup/MenuPage.test.ts | 306 +++++++++--------- webpack.dev.js | 15 - 10 files changed, 262 insertions(+), 298 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe6892f34..e9e90435a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,10 +9,10 @@ jobs: steps: - uses: actions/checkout@v2 - + - name: Setup Node.js environment uses: actions/setup-node@v2.1.2 - + - name: Install dependencies run: | npm i prettier @@ -23,20 +23,20 @@ jobs: build: runs-on: ubuntu-latest name: Build ${{ matrix.platform }} - + strategy: matrix: platform: ["chrome", "firefox"] - + steps: - uses: actions/checkout@v2 - + - name: Setup Node.js environment uses: actions/setup-node@v2.1.2 - + - name: Install dependencies run: npm ci - + - name: Build run: npm run ${{ matrix.platform }} run-tests: @@ -59,6 +59,3 @@ jobs: uses: mujo-code/puppeteer-headful@master with: args: npm test - - - name: Codecov - uses: codecov/codecov-action@v1.5.0 diff --git a/manifests/manifest-chrome-testing.json b/manifests/manifest-chrome-testing.json index 5f552fe2e..96d128949 100644 --- a/manifests/manifest-chrome-testing.json +++ b/manifests/manifest-chrome-testing.json @@ -45,8 +45,7 @@ }, "sandbox": { "pages": [ - "view/argon.html", - "view/test.html" + "view/argon.html" ] }, "permissions": [ @@ -69,8 +68,7 @@ "https://login.microsoftonline.com/common/oauth2/v2.0/token" ], "content_security_policy": { - "extension_pages": "script-src 'self'; font-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src https://www.google.com/ https://*.dropboxapi.com https://www.googleapis.com/ https://accounts.google.com/o/oauth2/revoke https://login.microsoftonline.com/common/oauth2/v2.0/token https://graph.microsoft.com/; default-src 'none'", - "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';" + "extension_pages": "script-src 'self'; font-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src https://www.google.com/ https://*.dropboxapi.com https://www.googleapis.com/ https://accounts.google.com/o/oauth2/revoke https://login.microsoftonline.com/common/oauth2/v2.0/token https://graph.microsoft.com/; default-src 'none'" }, "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjo5++7m6mlJGqKOnlYehr9tjIqahMZBJUG7PLa7dSRk6bDUu2pVodO1TQWviHlrDTLP+zfoVbDBS8v8cjloK5Tn90nzC6a957dPzOfyC1WUNYNDlGM0BCmZKVP/MWB3d0ffOmTwaxh0L47aLH5nTW0AUmuwCWCBEEl4Acuyp7rwLNGlazBpaom1Qb5ckn29gCJVVVIZ6wudmcrG/FPTNJXQbg8N6wObGrgGOaxmowbkzJmIfKTyHlYOKLAjZ7aJi0W6jsy47/aV+ojvn4gO+ka6BcRhUeWgoQxqEky119f3OWiVP46SJVbAi0pkknThUjDvX11lATGjB5EvJZGyotwIDAQAB" } diff --git a/package.json b/package.json index 9ec396497..179d34a17 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ }, "homepage": "https://github.com/Authenticator-Extension/Authenticator#readme", "devDependencies": { - "@jsdevtools/coverage-istanbul-loader": "^3.0.5", "@types/argon2-browser": "^1.18.1", "@types/chai": "^4.2.14", "@types/chrome": "^0.0.210", diff --git a/scripts/test-runner.ts b/scripts/test-runner.ts index c43627ade..d833ab6f7 100644 --- a/scripts/test-runner.ts +++ b/scripts/test-runner.ts @@ -23,7 +23,6 @@ interface StrippedTestResults { declare global { interface Window { __mocha_test_results__: MochaTestResults; - __coverage__: any; } } interface TestDisplay { @@ -44,12 +43,6 @@ async function runTests() { "--lang=en-US,en" ]; - if (!process.env.CI) { - // run with --single-process to prevent zombie Chromium processes from not terminating during development testing - // do not use this in CI as it will not run properly - puppeteerArgs.push("--single-process"); - } - const browser = await puppeteer.launch({ ignoreDefaultArgs: ["--disable-extensions"], args: puppeteerArgs, @@ -68,36 +61,25 @@ async function runTests() { } const results: { - coverage: unknown; testResults: MochaTestResults; } = await mochaPage.evaluate(() => { return new Promise((resolve: (value: { - coverage: unknown; testResults: MochaTestResults; }) => void) => { window.addEventListener("testsComplete", () => { resolve({ - coverage: window.__coverage__, testResults: window.__mocha_test_results__, }); }); if (window.__mocha_test_results__.completed) { resolve({ - coverage: window.__coverage__, testResults: window.__mocha_test_results__, }); } }); }); - if (process.env.CI) { - if (!fs.existsSync(".nyc_output")) fs.mkdirSync(".nyc_output"); - fs.writeFileSync(".nyc_output/out.json", JSON.stringify(results.coverage)); - const output = execSync("./node_modules/.bin/nyc report --reporter=text-lcov"); - fs.writeFileSync("coverage.lcov", output); - } - let failedTest = false; let display: TestDisplay = {}; if (results?.testResults.tests) { diff --git a/src/browser.ts b/src/browser.ts index c1f41edf8..c8b9fc7a2 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,10 +1,9 @@ -const userAgent = navigator.userAgent; - -export const isFirefox = userAgent.indexOf("Firefox") >= 0; -export const isWebKit = userAgent.indexOf("AppleWebKit") >= 0; +export const isFirefox = navigator.userAgent.indexOf("Firefox") >= 0; +export const isWebKit = navigator.userAgent.indexOf("AppleWebKit") >= 0; export const isEdge = navigator.userAgent.indexOf("Edg") >= 0; -export const isChromium = userAgent.indexOf("Chrome") >= 0; -export const isSafari = !isChromium && userAgent.indexOf("Safari") >= 0; +export const isChromium = navigator.userAgent.indexOf("Chrome") >= 0; +export const isSafari = + !isChromium && navigator.userAgent.indexOf("Safari") >= 0; export const isChrome = navigator.userAgent.indexOf("Chrome") !== -1 && navigator.userAgent.indexOf("Edg") === -1; diff --git a/src/components/Popup/MenuPage.vue b/src/components/Popup/MenuPage.vue index 84a8e0d56..864f3535c 100644 --- a/src/components/Popup/MenuPage.vue +++ b/src/components/Popup/MenuPage.vue @@ -121,7 +121,7 @@ export default Vue.extend({ openHelp() { let url = "https://otp.ee/chromeissues"; - if (isFirefox) { + if (navigator.userAgent.indexOf("Firefox") !== -1) { url = "https://otp.ee/firefoxissues"; } else if (navigator.userAgent.indexOf("Edg") !== -1) { url = "https://otp.ee/edgeissues"; diff --git a/src/test.ts b/src/test.ts index 834cb4a05..499bee7cb 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,5 +1,6 @@ import "mocha"; import { MochaReporter } from "./mochaReporter"; +import sinon from "sinon"; // @ts-expect-error this is not a node require const tests = require.context("./test", true, /\.tsx?$/); @@ -8,6 +9,11 @@ tests.keys().forEach(tests); mocha.setup({ // @ts-expect-error - typings are wrong reporter: MochaReporter, + rootHooks: { + afterEach() { + sinon.restore(); + }, + }, }); mocha.run(); diff --git a/src/test/components/Popup/EnterPasswordPage.test.ts b/src/test/components/Popup/EnterPasswordPage.test.ts index 7bf8bad81..ab1c16489 100644 --- a/src/test/components/Popup/EnterPasswordPage.test.ts +++ b/src/test/components/Popup/EnterPasswordPage.test.ts @@ -16,97 +16,93 @@ mocha.setup("bdd"); const localVue = createLocalVue(); describe("EnterPasswordPage", () => { - it("Tests disabled.", async () => { - return true; + before(async () => { + localVue.prototype.i18n = await loadI18nMessages(); + localVue.use(Vuex); + for (const component of CommonComponents) { + localVue.component(component.name, component.component); + } }); - // before(async () => { - // localVue.prototype.i18n = await loadI18nMessages(); - // localVue.use(Vuex); - // for (const component of CommonComponents) { - // localVue.component(component.name, component.component); - // } - // }); - - // let storeOpts = { - // modules: { - // accounts: { - // actions: { - // applyPassphrase: sinon.fake(), - // }, - // state: { - // wrongPassword: false, - // }, - // namespaced: true, - // }, - // }, - // }; - // let store: Store; - - // beforeEach(() => { - // // TODO: find a nicer var - // storeOpts.modules.accounts.actions.applyPassphrase.resetHistory(); - // store = new Vuex.Store(storeOpts); - // }); - - // it("should apply password when button is clicked", async () => { - // const wrapper = mount(EnterPasswordPage, { store, localVue }); - - // const passwordInput = wrapper.find("input"); - // const passwordButton = wrapper.find("button"); - - // passwordInput.setValue("somePassword"); - // await passwordButton.trigger("click"); - // storeOpts.modules.accounts.actions.applyPassphrase.should.have.been.calledWith( - // sinon.match.any, - // "somePassword" - // ); - // }); - - // it("should apply password when enter is pressed", async () => { - // const wrapper = mount(EnterPasswordPage, { store, localVue }); - - // const passwordInput = wrapper.find("input"); - - // passwordInput.setValue("anotherPassword"); - // await passwordInput.trigger("keyup.enter"); - // storeOpts.modules.accounts.actions.applyPassphrase.should.have.been.calledWith( - // sinon.match.any, - // "anotherPassword" - // ); - // }); - - // it("should autofocus password input", () => { - // const wrapper = mount(EnterPasswordPage, { - // store, - // localVue, - // attachToDocument: true, - // }); - - // const passwordInput = wrapper.find("input"); - - // passwordInput.element.should.eq(document.activeElement); - // }); - - // it("should not show incorrect password message", () => { - // const wrapper = mount(EnterPasswordPage, { store, localVue }); - - // const errorText = wrapper.find("label.warning"); - - // errorText.isVisible().should.be.false; - // }); - - // context("Incorrect password was entered", () => { - // before(() => { - // storeOpts.modules.accounts.state.wrongPassword = true; - // }); - - // it("should show incorrect password message", () => { - // const wrapper = mount(EnterPasswordPage, { store, localVue }); - - // const errorText = wrapper.find("label.warning"); - - // errorText.isVisible().should.be.true; - // }); - // }); + let storeOpts = { + modules: { + accounts: { + actions: { + applyPassphrase: sinon.fake(), + }, + state: { + wrongPassword: false, + }, + namespaced: true, + }, + }, + }; + let store: Store; + + beforeEach(() => { + // TODO: find a nicer var + storeOpts.modules.accounts.actions.applyPassphrase.resetHistory(); + store = new Vuex.Store(storeOpts); + }); + + it("should apply password when button is clicked", async () => { + const wrapper = mount(EnterPasswordPage, { store, localVue }); + + const passwordInput = wrapper.find("input"); + const passwordButton = wrapper.find("button"); + + passwordInput.setValue("somePassword"); + await passwordButton.trigger("click"); + storeOpts.modules.accounts.actions.applyPassphrase.should.have.been.calledWith( + sinon.match.any, + "somePassword" + ); + }); + + it("should apply password when enter is pressed", async () => { + const wrapper = mount(EnterPasswordPage, { store, localVue }); + + const passwordInput = wrapper.find("input"); + + passwordInput.setValue("anotherPassword"); + await passwordInput.trigger("keyup.enter"); + storeOpts.modules.accounts.actions.applyPassphrase.should.have.been.calledWith( + sinon.match.any, + "anotherPassword" + ); + }); + + it("should autofocus password input", () => { + const wrapper = mount(EnterPasswordPage, { + store, + localVue, + attachToDocument: true, + }); + + const passwordInput = wrapper.find("input"); + + passwordInput.element.should.eq(document.activeElement); + }); + + it("should not show incorrect password message", () => { + const wrapper = mount(EnterPasswordPage, { store, localVue }); + + const errorText = wrapper.find("label.warning"); + + errorText.isVisible().should.be.false; + }); + + context("Incorrect password was entered", () => { + before(() => { + storeOpts.modules.accounts.state.wrongPassword = true; + }); + + it("should show incorrect password message", () => { + const wrapper = mount(EnterPasswordPage, { store, localVue }); + + const errorText = wrapper.find("label.warning"); + + errorText.isVisible().should.be.true; + }); + }); }); diff --git a/src/test/components/Popup/MenuPage.test.ts b/src/test/components/Popup/MenuPage.test.ts index 5089f47a9..3fa84b000 100644 --- a/src/test/components/Popup/MenuPage.test.ts +++ b/src/test/components/Popup/MenuPage.test.ts @@ -17,6 +17,7 @@ import { Notification } from "../../../store/Notification"; import { Qr } from "../../../store/Qr"; import chrome from "sinon-chrome"; +import sinon from "sinon"; chai.should(); chai.use(sinonChai); @@ -24,159 +25,160 @@ mocha.setup("bdd"); const localVue = createLocalVue(); describe("MenuPage", () => { - it("Tests disabled.", async () => { + before(async () => { + localVue.prototype.i18n = await loadI18nMessages(); + localVue.use(Vuex); + }); + + let storeOpts = { + menu: { + state: { + version: "1.2.3", + }, + namespaced: true, + }, + }; + + let store: Store<{}>; + + let wrapper: Wrapper; + + before(() => { + // mock the chrome global object + global.chrome.tabs.create = chrome.tabs.create; + global.chrome.storage.managed.get = chrome.storage.managed.get; + }); + + beforeEach(async () => { + store = new Vuex.Store({ + modules: storeOpts, + }); + wrapper = mount(MenuPage, { + store, + localVue, + }); + }); + + const clickMenuPageButtonByTitle = async ( + wrapper: Wrapper, + title: string + ) => wrapper.find(`*[title='${title}']`).trigger("click"); + + it("Tests disabled", async () => { return true; }); - // before(async () => { - // localVue.prototype.i18n = await loadI18nMessages(); - // localVue.use(Vuex); - // }); - - // let storeOpts = { - // menu: { - // state: { - // version: "1.2.3", - // }, - // namespaced: true, - // }, - // }; - - // let store: Store<{}>; - - // let wrapper: Wrapper; - - // before(() => { - // // mock the chrome global object - // global.chrome.tabs.create = chrome.tabs.create; - // global.chrome.storage.managed.get = chrome.storage.managed.get; - // }); - - // beforeEach(async () => { - // store = new Vuex.Store({ - // modules: storeOpts, - // }); - // wrapper = mount(MenuPage, { - // store, - // localVue, - // }); - // }); - - // const clickMenuPageButtonByTitle = async ( - // wrapper: Wrapper, - // title: string - // ) => wrapper.find(`*[title='${title}']`).trigger("click"); - - // describe("feedback button", () => { - // // mocks the user agent for testing purposes - // const mockUserAgent = (userAgent: string) => { - // Object.defineProperty(global, "navigator", { - // value: { - // userAgent, - // }, - // configurable: true, - // enumerable: true, - // writable: true, - // }); - // }; - - // beforeEach(() => { - // wrapper = mount(MenuPage, { - // store, - // localVue, - // }); - // }); - - // it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is Chrome", async () => { - // mockUserAgent( - // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" - // ); - // await clickMenuPageButtonByTitle(wrapper, "Feedback"); - // assert.ok( - // chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) - // .calledOnce, - // "Tab create should be called with the Chrome URL" - // ); - // }); - - // it("should open a new tab to the Edge help page when the feedback button is clicked and the user agent is Edge", async () => { - // mockUserAgent( - // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43" - // ); - // await clickMenuPageButtonByTitle(wrapper, "Feedback"); - // assert.ok( - // chrome.tabs.create.withArgs({ url: "https://otp.ee/edgeissues" }) - // .calledOnce, - // "Tab create should be called with the Edge URL" - // ); - // }); - - // it("should open a new tab to the Firefox help page when the feedback button is clicked and the user agent is Firefox", async () => { - // mockUserAgent( - // "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0" - // ); - // await clickMenuPageButtonByTitle(wrapper, "Feedback"); - // assert.ok( - // chrome.tabs.create.withArgs({ url: "https://otp.ee/firefoxissues" }) - // .calledOnce, - // "Tab create should be called with the Firefox URL" - // ); - // }); - - // it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is unknown", async () => { - // mockUserAgent("Unknown"); - // await clickMenuPageButtonByTitle(wrapper, "Feedback"); - // assert.ok( - // chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) - // .called, - // "Tab create should be called with the Chrome URL" - // ); - // }); - - // describe("feedbackURL is set", () => { - // beforeEach(async () => { - // try { - // chrome.storage.managed.get.yieldsAsync({ - // feedbackURL: "https://authenticator.cc", - // }); - - // store = new Vuex.Store({ - // modules: { - // backup: await new Backup().getModule(), - // currentView: new CurrentView().getModule(), - // notification: new Notification().getModule(), - // qr: new Qr().getModule(), - // style: new Style().getModule(), - // menu: await new Menu().getModule(), - // accounts: await new Accounts().getModule(), - // }, - // }); - - // wrapper = mount(MenuPage, { - // store, - // localVue, - // }); - // } catch (e) { - // console.error(e); - // // Doesn't show up in mocha? - // throw e; - // } - // }); - - // it("should open a new tab to the page specified in ManagedStorage", async () => { - // await clickMenuPageButtonByTitle(wrapper, "Feedback"); - // assert.ok( - // chrome.tabs.create.withArgs({ url: "https://authenticator.cc" }) - // .called, - // "Tab create should be called with the feedback URL" - // ); - // }); - // }); - // }); - - // describe("extension version", () => { - // it("should be displayed", () => { - // assert.equal(wrapper.find("#version").text(), "Version 1.2.3"); - // }); - // }); + describe("feedback button", () => { + // mocks the user agent for testing purposes + const mockUserAgent = (userAgent: string) => { + Object.defineProperty(global, "navigator", { + value: { + userAgent, + }, + configurable: true, + enumerable: true, + writable: true, + }); + }; + + beforeEach(() => { + wrapper = mount(MenuPage, { + store, + localVue, + }); + }); + + it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is Chrome", async () => { + mockUserAgent( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" + ); + await clickMenuPageButtonByTitle(wrapper, "Feedback"); + assert.ok( + chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) + .calledOnce, + "Tab create should be called with the Chrome URL" + ); + }); + + it("should open a new tab to the Edge help page when the feedback button is clicked and the user agent is Edge", async () => { + mockUserAgent( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43" + ); + await clickMenuPageButtonByTitle(wrapper, "Feedback"); + assert.ok( + chrome.tabs.create.withArgs({ url: "https://otp.ee/edgeissues" }) + .calledOnce, + "Tab create should be called with the Edge URL" + ); + }); + + it("should open a new tab to the Firefox help page when the feedback button is clicked and the user agent is Firefox", async () => { + mockUserAgent( + "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0" + ); + await clickMenuPageButtonByTitle(wrapper, "Feedback"); + console.log("TESTESFDGSDG"); + assert.ok( + chrome.tabs.create.withArgs({ url: "https://otp.ee/firefoxissues" }) + .calledOnce, + "Tab create should be called with the Firefox URL" + ); + }); + + it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is unknown", async () => { + mockUserAgent("Unknown"); + await clickMenuPageButtonByTitle(wrapper, "Feedback"); + assert.ok( + chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) + .called, + "Tab create should be called with the Chrome URL" + ); + }); + + describe("feedbackURL is set", () => { + beforeEach(async () => { + try { + chrome.storage.managed.get.yieldsAsync({ + feedbackURL: "https://authenticator.cc", + }); + + store = new Vuex.Store({ + modules: { + backup: await new Backup().getModule(), + currentView: new CurrentView().getModule(), + notification: new Notification().getModule(), + qr: new Qr().getModule(), + style: new Style().getModule(), + menu: await new Menu().getModule(), + accounts: await new Accounts().getModule(), + }, + }); + + wrapper = mount(MenuPage, { + store, + localVue, + }); + } catch (e) { + console.error(e); + // Doesn't show up in mocha? + throw e; + } + }); + + it("should open a new tab to the page specified in ManagedStorage", async () => { + await clickMenuPageButtonByTitle(wrapper, "Feedback"); + assert.ok( + chrome.tabs.create.withArgs({ url: "https://authenticator.cc" }) + .called, + "Tab create should be called with the feedback URL" + ); + }); + }); + }); + + describe("extension version", () => { + it("should be displayed", () => { + assert.equal(wrapper.find("#version").text(), "Version 1.2.3"); + }); + }); }); diff --git a/webpack.dev.js b/webpack.dev.js index a716fb1b4..bf3fc5f6f 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -24,21 +24,6 @@ module.exports = merge(common, { }), ], module: { - rules: [ - { - // Note that while it is possible to instrument .vue files, it does not produce correct output. Do not show *.vue in coverage reports. - loader: '@jsdevtools/coverage-istanbul-loader', - options: { esModules: true }, - include: path.resolve(__dirname, 'src/'), - exclude: [ - path.resolve(__dirname, 'src/test/'), - path.resolve(__dirname, 'src/test.ts'), - path.resolve(__dirname, 'src/mochaReporter.ts'), - path.resolve(__dirname, 'node_modules/'), - ], - enforce: "post" - } - ], // to suppress mocha warnings exprContextCritical: false, },