From 8c0c1c0568d95ea01869a609eefb8d0527948f03 Mon Sep 17 00:00:00 2001 From: Kumar McMillan Date: Mon, 13 Nov 2017 12:06:32 -0600 Subject: [PATCH 1/4] Quantum compat is true for all webext / moz addons --- src/core/utils/compatibility.js | 36 +++----- tests/unit/amo/helpers.js | 1 + tests/unit/core/utils/test_compatibility.js | 92 ++++++++++++++++++++- 3 files changed, 102 insertions(+), 27 deletions(-) diff --git a/src/core/utils/compatibility.js b/src/core/utils/compatibility.js index 944ded7ce21..718f5d6d58e 100644 --- a/src/core/utils/compatibility.js +++ b/src/core/utils/compatibility.js @@ -1,13 +1,10 @@ /* global window */ import { oneLine } from 'common-tags'; import mozCompare from 'mozilla-version-comparator'; -import UAParser from 'ua-parser-js'; import { ADDON_TYPE_EXTENSION, ADDON_TYPE_OPENSEARCH, - CLIENT_APP_ANDROID, - CLIENT_APP_FIREFOX, INCOMPATIBLE_FIREFOX_FOR_IOS, INCOMPATIBLE_NO_OPENSEARCH, INCOMPATIBLE_NOT_FIREFOX, @@ -165,25 +162,16 @@ export function getClientCompatibility({ } export const isQuantumCompatible = ({ addon }) => { - // HACK: we use a hardcoded User Agent string corresponding to MacOS/Firefox - // 57.0 to determine whether the given `addon` is compatible with Quantum. - // The OS is not important as far as it is not `iOS`. - // - // See: https://github.com/mozilla/addons-frontend/issues/3868. - const userAgentInfo = UAParser(oneLine`Mozilla/5.0 (Macintosh; Intel Mac OS X - 10.12; rv:57.0) Gecko/20100101 Firefox/57.0`); - - const supportsDesktop57 = getClientCompatibility({ - addon, - clientApp: CLIENT_APP_FIREFOX, - userAgentInfo, - }).compatible; - - const supportsAndroid57 = getClientCompatibility({ - addon, - clientApp: CLIENT_APP_ANDROID, - userAgentInfo, - }).compatible; - - return supportsDesktop57 || supportsAndroid57; + // TODO: refactor code that inspects the real compatibility + // object and re-use that logic to accomplish this instead. + // https://github.com/mozilla/addons-frontend/issues/3814 + + if (!addon.current_version) { + return false; + } + return addon.current_version.files.some((file) => { + // These checks are fragile because future mozilla-signed extensions + // may not be Quantum compatible. + return file.is_webextension || file.is_mozilla_signed_extension; + }); }; diff --git a/tests/unit/amo/helpers.js b/tests/unit/amo/helpers.js index 349dbcf2dc0..96901e46887 100644 --- a/tests/unit/amo/helpers.js +++ b/tests/unit/amo/helpers.js @@ -56,6 +56,7 @@ export const fakeAddon = Object.freeze({ created: '2014-11-22T10:09:01Z', hash: 'a1b2c3d4', id: 57721, + is_mozilla_signed_extension: false, is_restart_required: false, is_webextension: true, permissions: ['activeTab', 'webRequest'], diff --git a/tests/unit/core/utils/test_compatibility.js b/tests/unit/core/utils/test_compatibility.js index db884892024..48330c04503 100644 --- a/tests/unit/core/utils/test_compatibility.js +++ b/tests/unit/core/utils/test_compatibility.js @@ -13,7 +13,11 @@ import { INCOMPATIBLE_OVER_MAX_VERSION, INCOMPATIBLE_UNDER_MIN_VERSION, INCOMPATIBLE_UNSUPPORTED_PLATFORM, + OS_ALL, + OS_ANDROID, + OS_LINUX, OS_MAC, + OS_WINDOWS, } from 'core/constants'; import { createInternalAddon } from 'core/reducers/addons'; import { @@ -616,8 +620,33 @@ describe(__filename, () => { }); describe('isQuantumCompatible', () => { - it('returns `true` when add-on is compatible', () => { + it('returns `true` when webextension is compatible', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: false, + is_webextension: true, + platform: OS_ALL, + }], + name: 'Some Quantum WebExtension', + compatibility: { + [CLIENT_APP_FIREFOX]: { + max: '*', + min: '53.0', + }, + }, + is_strict_compatibility_enabled: false, + })); + + expect(isQuantumCompatible({ addon })).toEqual(true); + }); + + it('returns `true` when mozilla extension is compatible', () => { + const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: true, + is_webextension: false, + platform: OS_ALL, + }], name: 'Firefox Multi-Account Containers', compatibility: { [CLIENT_APP_FIREFOX]: { @@ -631,8 +660,53 @@ describe(__filename, () => { expect(isQuantumCompatible({ addon })).toEqual(true); }); - it('returns `false` when add-on is not compatible', () => { + it('returns `true` for windows-only mozilla extensions', () => { + const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: true, + is_webextension: false, + platform: OS_WINDOWS, + }], + name: 'Windows only mozilla extension', + compatibility: { + [CLIENT_APP_FIREFOX]: { + max: '*', + min: '53.0', + }, + }, + is_strict_compatibility_enabled: false, + })); + + expect(isQuantumCompatible({ addon })).toEqual(true); + }); + + it('returns `true` for linux-only mozilla extensions', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: true, + is_webextension: false, + platform: OS_LINUX, + }], + name: 'Linux only mozilla extension', + compatibility: { + [CLIENT_APP_FIREFOX]: { + max: '*', + min: '53.0', + }, + }, + is_strict_compatibility_enabled: false, + })); + + expect(isQuantumCompatible({ addon })).toEqual(true); + }); + + it('returns `false` when non-webextesion is not compatible', () => { + const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: false, + is_webextension: false, + platform: OS_ALL, + }], name: 'Firebug', compatibility: { [CLIENT_APP_FIREFOX]: { @@ -646,8 +720,20 @@ describe(__filename, () => { expect(isQuantumCompatible({ addon })).toEqual(false); }); - it('returns `true` when add-on is compatible with one of the platforms', () => { + it('returns `false` for add-ons without a current version', () => { + const addon = createInternalAddon(createFakeAddon({ + current_version: null, + })); + + expect(isQuantumCompatible({ addon })).toEqual(false); + }); + + it('returns `true` when Android webextension is compatible', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_webextension: true, + platform: OS_ANDROID, + }], compatibility: { // This platform is not compatible... [CLIENT_APP_FIREFOX]: { From 23963f638d35375a6a8259ca81711349af44bb54 Mon Sep 17 00:00:00 2001 From: Kumar McMillan Date: Mon, 13 Nov 2017 12:26:06 -0600 Subject: [PATCH 2/4] Fix badges test --- tests/unit/amo/components/TestAddonBadges.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/amo/components/TestAddonBadges.js b/tests/unit/amo/components/TestAddonBadges.js index c9a722c720d..08e0c300f7f 100644 --- a/tests/unit/amo/components/TestAddonBadges.js +++ b/tests/unit/amo/components/TestAddonBadges.js @@ -121,6 +121,9 @@ describe(__filename, () => { describe('Quantum compatible badge', () => { it('does not display a badge when add-on is compatible with Quantum', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_webextension: true, + }], compatibility: { [CLIENT_APP_FIREFOX]: { max: '*', @@ -137,6 +140,10 @@ describe(__filename, () => { it('displays a badge when the addon is not compatible with Quantum', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_mozilla_signed_extension: false, + is_webextension: false, + }], compatibility: { [CLIENT_APP_FIREFOX]: { max: '56.*', @@ -156,6 +163,9 @@ describe(__filename, () => { it('does not display a badge for add-ons that are not extensions', () => { const addon = createInternalAddon(createFakeAddon({ + files: [{ + is_webextension: false, + }], type: ADDON_TYPE_THEME, compatibility: { [CLIENT_APP_FIREFOX]: { From 6df0f32bbb8dc67f9dd48a822fcbed437ab047ad Mon Sep 17 00:00:00 2001 From: Kumar McMillan Date: Mon, 13 Nov 2017 12:48:04 -0600 Subject: [PATCH 3/4] Use computed helper props --- src/core/reducers/addons.js | 7 +++ src/core/types/addons.js | 2 + src/core/utils/compatibility.js | 11 ++-- tests/unit/core/reducers/test_addons.js | 55 +++++++++++++++++--- tests/unit/disco/containers/TestDiscoPane.js | 1 + 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/core/reducers/addons.js b/src/core/reducers/addons.js index b400a82d493..2ec4d0d580f 100644 --- a/src/core/reducers/addons.js +++ b/src/core/reducers/addons.js @@ -232,6 +232,7 @@ export function createInternalAddon( }, isRestartRequired: false, isWebExtension: false, + isMozillaSignedExtension: false, }; if (addon.type === ADDON_TYPE_THEME && apiAddon.theme_data) { @@ -264,9 +265,15 @@ export function createInternalAddon( addon.isRestartRequired = apiAddon.current_version.files.some( (file) => !!file.is_restart_required ); + // The following checks are a bit fragile since only one file needs + // to contain the flag. However, it is highly unlikely to create an + // add-on with mismatched file flags in the current DevHub. addon.isWebExtension = apiAddon.current_version.files.some( (file) => !!file.is_webextension ); + addon.isMozillaSignedExtension = apiAddon.current_version.files.some( + (file) => !!file.is_mozilla_signed_extension + ); } // Remove undefined properties entirely. This is for some legacy code diff --git a/src/core/types/addons.js b/src/core/types/addons.js index eae482eb170..9a0f82ba2db 100644 --- a/src/core/types/addons.js +++ b/src/core/types/addons.js @@ -22,6 +22,7 @@ type AddonFileType = {| created: string, hash: string, id: number, + is_mozilla_signed_extension: boolean, is_restart_required: boolean, is_webextension: boolean, permissions?: Array, @@ -152,6 +153,7 @@ export type AddonType = { linux: ?string, windows: ?string, }, + isMozillaSignedExtension: boolean, isRestartRequired: boolean, isWebExtension: boolean, themeData?: ThemeData, diff --git a/src/core/utils/compatibility.js b/src/core/utils/compatibility.js index 718f5d6d58e..ec2f2dce4e5 100644 --- a/src/core/utils/compatibility.js +++ b/src/core/utils/compatibility.js @@ -166,12 +166,7 @@ export const isQuantumCompatible = ({ addon }) => { // object and re-use that logic to accomplish this instead. // https://github.com/mozilla/addons-frontend/issues/3814 - if (!addon.current_version) { - return false; - } - return addon.current_version.files.some((file) => { - // These checks are fragile because future mozilla-signed extensions - // may not be Quantum compatible. - return file.is_webextension || file.is_mozilla_signed_extension; - }); + // These checks are fragile because future mozilla-signed extensions + // may not be Quantum compatible. + return addon.isWebExtension || addon.isMozillaSignedExtension; }; diff --git a/tests/unit/core/reducers/test_addons.js b/tests/unit/core/reducers/test_addons.js index 055fc46f79d..54f8eb33ea7 100644 --- a/tests/unit/core/reducers/test_addons.js +++ b/tests/unit/core/reducers/test_addons.js @@ -79,6 +79,7 @@ describe(__filename, () => { }, isRestartRequired: false, isWebExtension: true, + isMozillaSignedExtension: false, }); }); @@ -109,6 +110,7 @@ describe(__filename, () => { }, isRestartRequired: false, isWebExtension: true, + isMozillaSignedExtension: false, }; delete expectedTheme.theme_data; @@ -278,9 +280,7 @@ describe(__filename, () => { it('exposes `isWebExtension` attribute from current version files', () => { const addon = createFakeAddon({ - files: [ - { ...fakeAddon.current_version.files[0], is_webextension: true }, - ], + files: [{ is_webextension: true }], }); const state = addons(undefined, @@ -290,9 +290,7 @@ describe(__filename, () => { it('sets `isWebExtension` to `false` when add-on is not a web extension', () => { const addon = createFakeAddon({ - files: [ - { ...fakeAddon.current_version.files[0], is_webextension: false }, - ], + files: [{ is_webextension: false }], }); const state = addons(undefined, @@ -311,8 +309,8 @@ describe(__filename, () => { it('sets `isWebExtension` to `true` when any file declares it', () => { const addon = createFakeAddon({ files: [ - { ...fakeAddon.current_version.files[0], is_webextension: false }, - { ...fakeAddon.current_version.files[0], is_webextension: true }, + { is_webextension: false }, + { is_webextension: true }, ], }); @@ -321,6 +319,47 @@ describe(__filename, () => { expect(state[addon.slug].isWebExtension).toBe(true); }); + it('exposes `isMozillaSignedExtension` from current version files', () => { + const addon = createFakeAddon({ + files: [{ is_mozilla_signed_extension: true }], + }); + + const state = addons(undefined, + loadAddons(createFetchAddonResult(addon).entities)); + expect(state[addon.slug].isMozillaSignedExtension).toBe(true); + }); + + it('sets `isMozillaSignedExtension` to `false` when not a web extension', () => { + const addon = createFakeAddon({ + files: [{ is_mozilla_signed_extension: false }], + }); + + const state = addons(undefined, + loadAddons(createFetchAddonResult(addon).entities)); + expect(state[addon.slug].isMozillaSignedExtension).toBe(false); + }); + + it('sets `isMozillaSignedExtension` to `false` without files', () => { + const addon = createFakeAddon({ files: [] }); + + const state = addons(undefined, + loadAddons(createFetchAddonResult(addon).entities)); + expect(state[addon.slug].isMozillaSignedExtension).toBe(false); + }); + + it('sets `isMozillaSignedExtension` to `true` when any file declares it', () => { + const addon = createFakeAddon({ + files: [ + { is_mozilla_signed_extension: false }, + { is_mozilla_signed_extension: true }, + ], + }); + + const state = addons(undefined, + loadAddons(createFetchAddonResult(addon).entities)); + expect(state[addon.slug].isMozillaSignedExtension).toBe(true); + }); + describe('fetchAddon', () => { const defaultParams = Object.freeze({ slug: 'addon-slug', diff --git a/tests/unit/disco/containers/TestDiscoPane.js b/tests/unit/disco/containers/TestDiscoPane.js index 6d0b2f9f2d3..83a004f21da 100644 --- a/tests/unit/disco/containers/TestDiscoPane.js +++ b/tests/unit/disco/containers/TestDiscoPane.js @@ -148,6 +148,7 @@ describe(__filename, () => { mac: undefined, windows: undefined, }, + isMozillaSignedExtension: false, isRestartRequired: false, isWebExtension: true, }]); From 87439641ecdda78fa10e0936ad807690eb3a643a Mon Sep 17 00:00:00 2001 From: Kumar McMillan Date: Mon, 13 Nov 2017 12:51:30 -0600 Subject: [PATCH 4/4] fix test spec --- tests/unit/core/reducers/test_addons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/core/reducers/test_addons.js b/tests/unit/core/reducers/test_addons.js index 54f8eb33ea7..dd887101794 100644 --- a/tests/unit/core/reducers/test_addons.js +++ b/tests/unit/core/reducers/test_addons.js @@ -329,7 +329,7 @@ describe(__filename, () => { expect(state[addon.slug].isMozillaSignedExtension).toBe(true); }); - it('sets `isMozillaSignedExtension` to `false` when not a web extension', () => { + it('sets `isMozillaSignedExtension` to `false` when not declared', () => { const addon = createFakeAddon({ files: [{ is_mozilla_signed_extension: false }], });