From d4f2f3f29df1443a68497479e23e2d0f459aa628 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Tue, 14 Jan 2025 14:01:32 +0000
Subject: [PATCH 01/20] feat(27255): allow local modification for remote
feature flags
---
app/scripts/lib/manifestFlags.ts | 12 +
development/build/manifest.js | 5 +-
.../utils/plugins/ManifestPlugin/helpers.ts | 13 +
lavamoat/browserify/beta/policy.json | 2 +-
lavamoat/browserify/flask/policy.json | 2 +-
lavamoat/browserify/main/policy.json | 2 +-
lavamoat/browserify/mmi/policy.json | 2 +-
lavamoat/build-system/policy.json | 354 ++++++++++++++----
manifest-flags.json | 8 +
test/e2e/constants.ts | 18 +
.../pages/developer-options-page.ts | 17 +
.../remote-feature-flag.spec.ts | 39 +-
.../developer-options-tab.test.tsx.snap | 39 ++
.../developer-options-tab.test.tsx | 17 +-
.../developer-options-tab.tsx | 45 ++-
.../settings/info-tab/info-tab.component.js | 12 -
.../settings/info-tab/info-tab.stories.js | 2 +-
ui/pages/settings/info-tab/info-tab.test.tsx | 4 +-
ui/pages/settings/settings.component.js | 9 +-
ui/pages/settings/settings.container.js | 4 -
ui/pages/settings/settings.stories.js | 1 -
ui/pages/settings/settings.test.js | 1 -
ui/selectors/selectors.js | 17 +
ui/selectors/selectors.test.js | 62 ++-
24 files changed, 564 insertions(+), 123 deletions(-)
create mode 100644 manifest-flags.json
diff --git a/app/scripts/lib/manifestFlags.ts b/app/scripts/lib/manifestFlags.ts
index 574099d0cb94..8e6848be7ade 100644
--- a/app/scripts/lib/manifestFlags.ts
+++ b/app/scripts/lib/manifestFlags.ts
@@ -60,6 +60,18 @@ export type ManifestFlags = {
*/
forceEnable?: boolean;
};
+ /**
+ * Feature flags to control business logic behavior
+ */
+ remoteFeatureFlags?: {
+ /**
+ * A test remote featureflag for threshold
+ */
+ testFlagForThreshold: {
+ name: string;
+ value: string;
+ };
+ };
};
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- you can't extend a type, we want this to be an interface
diff --git a/development/build/manifest.js b/development/build/manifest.js
index bc5325b372eb..17f6168df7dc 100644
--- a/development/build/manifest.js
+++ b/development/build/manifest.js
@@ -11,6 +11,7 @@ const baradDurManifest = isManifestV3
? require('../../app/manifest/v3/_barad_dur.json')
: require('../../app/manifest/v2/_barad_dur.json');
const { loadBuildTypesConfig } = require('../lib/build-type');
+const manifestFlags = require('../../manifest-flags.json');
const { TASKS, ENVIRONMENT } = require('./constants');
const { createTask, composeSeries } = require('./task');
@@ -47,8 +48,10 @@ function createManifestTasks({
browserVersionMap[platform],
await getBuildModifications(buildType, platform),
customArrayMerge,
+ {
+ _flags: manifestFlags,
+ },
);
-
modifyNameAndDescForNonProd(result);
const dir = path.join('.', 'dist', platform);
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index 82efa9acf253..1db54118996e 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -1,3 +1,5 @@
+import manifestFlags from '../../../../../manifest-flags.json';
+
/**
* Returns a function that will transform a manifest JSON object based on the
* given build args.
@@ -24,6 +26,17 @@ export function transformManifest(args: { lockdown: boolean; test: boolean }) {
}
}
+ /**
+ * This function sets predefined flags in the manifest's _flags property.
+ *
+ * @param browserManifest - The Chrome extension manifest object to modify
+ */
+ function addManifestFlags(browserManifest: chrome.runtime.Manifest) {
+ browserManifest._flags = manifestFlags;
+ }
+
+ transforms.push(addManifestFlags);
+
if (!args.lockdown) {
// remove lockdown scripts from content_scripts
transforms.push(removeLockdown);
diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json
index c4985af54a1d..dc35b61fdd86 100644
--- a/lavamoat/browserify/beta/policy.json
+++ b/lavamoat/browserify/beta/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json
index c4985af54a1d..dc35b61fdd86 100644
--- a/lavamoat/browserify/flask/policy.json
+++ b/lavamoat/browserify/flask/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json
index c4985af54a1d..dc35b61fdd86 100644
--- a/lavamoat/browserify/main/policy.json
+++ b/lavamoat/browserify/main/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json
index bc5be17e5a64..c5d67dc34329 100644
--- a/lavamoat/browserify/mmi/policy.json
+++ b/lavamoat/browserify/mmi/policy.json
@@ -5867,4 +5867,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json
index 545922814d1f..7fc9728c7c90 100644
--- a/lavamoat/build-system/policy.json
+++ b/lavamoat/build-system/policy.json
@@ -5,7 +5,7 @@
"define": true
},
"packages": {
- "terser>@jridgewell/source-map>@jridgewell/gen-mapping": true,
+ "@babel/core>@ampproject/remapping>@jridgewell/gen-mapping": true,
"terser-webpack-plugin>@jridgewell/trace-mapping": true
}
},
@@ -60,7 +60,7 @@
"@babel/preset-react": true,
"@babel/preset-typescript": true,
"@babel/core>@babel/template": true,
- "depcheck>@babel/traverse": true,
+ "@babel/core>@babel/traverse": true,
"@babel/core>@babel/types": true,
"@babel/core>convert-source-map": true,
"nock>debug": true,
@@ -147,7 +147,7 @@
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true,
"@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true,
- "depcheck>@babel/traverse": true,
+ "@babel/core>@babel/traverse": true,
"@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin>semver": true
}
},
@@ -175,7 +175,7 @@
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/helper-define-polyfill-provider>lodash.debounce": true,
- "depcheck>resolve": true
+ "browserify>resolve": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": {
@@ -200,9 +200,8 @@
"packages": {
"@babel/core": true,
"@babel/core>@babel/helper-module-transforms>@babel/helper-module-imports": true,
- "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": true,
"@babel/code-frame>@babel/helper-validator-identifier": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": {
@@ -215,7 +214,7 @@
"@babel/core": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true,
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": {
@@ -223,10 +222,10 @@
"@babel/core": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
- "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": {
+ "@babel/preset-env>@babel/plugin-transform-modules-commonjs>@babel/helper-simple-access": {
"packages": {
"@babel/core>@babel/types": true
}
@@ -239,7 +238,7 @@
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": {
"packages": {
"@babel/core>@babel/template": true,
- "depcheck>@babel/traverse": true,
+ "@babel/core>@babel/traverse": true,
"@babel/core>@babel/types": true
}
},
@@ -252,7 +251,7 @@
"@babel/preset-env>@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
"packages": {
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-bugfix-safari-class-field-initializer-scope": {
@@ -278,7 +277,7 @@
"packages": {
"@babel/core": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-syntax-import-assertions": {
@@ -317,7 +316,7 @@
"@babel/core": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-async-to-generator": {
@@ -359,8 +358,8 @@
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true,
- "depcheck>@babel/traverse": true,
- "@babel/preset-env>@babel/plugin-transform-classes>globals": true
+ "@babel/core>@babel/traverse": true,
+ "depcheck>@babel/traverse>globals": true
}
},
"@babel/preset-env>@babel/plugin-transform-computed-properties": {
@@ -423,7 +422,7 @@
"packages": {
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-json-strings": {
@@ -461,7 +460,7 @@
"@babel/core": true,
"@babel/core>@babel/helper-module-transforms": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": true
+ "@babel/preset-env>@babel/plugin-transform-modules-commonjs>@babel/helper-simple-access": true
}
},
"@babel/preset-env>@babel/plugin-transform-modules-systemjs": {
@@ -473,7 +472,7 @@
"@babel/core>@babel/helper-module-transforms": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/code-frame>@babel/helper-validator-identifier": true,
- "depcheck>@babel/traverse": true
+ "@babel/core>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-modules-umd": {
@@ -780,7 +779,7 @@
"@babel/core>@babel/types": true
}
},
- "depcheck>@babel/traverse": {
+ "@babel/core>@babel/traverse": {
"globals": {
"console.log": true
},
@@ -790,7 +789,25 @@
"@babel/core>@babel/parser": true,
"@babel/core>@babel/template": true,
"@babel/core>@babel/types": true,
- "babel/preset-env>b@babel/types": true,
+ "nock>debug": true,
+ "depcheck>@babel/traverse>globals": true
+ }
+ },
+ "depcheck>@babel/traverse": {
+ "packages": {
+ "babel/preset-env>b@babel/types": true
+ }
+ },
+ "lavamoat>lavamoat-tofu>@babel/traverse": {
+ "globals": {
+ "console.log": true
+ },
+ "packages": {
+ "@babel/code-frame": true,
+ "@babel/core>@babel/generator": true,
+ "lavamoat>lavamoat-tofu>@babel/traverse>@babel/parser": true,
+ "@babel/core>@babel/template": true,
+ "lavamoat>lavamoat-tofu>@babel/traverse>@babel/types": true,
"nock>debug": true,
"depcheck>@babel/traverse>globals": true
}
@@ -805,6 +822,16 @@
"@babel/code-frame>@babel/helper-validator-identifier": true
}
},
+ "lavamoat>lavamoat-tofu>@babel/traverse>@babel/types": {
+ "globals": {
+ "console.warn": true,
+ "process.env": true
+ },
+ "packages": {
+ "@babel/core>@babel/types>@babel/helper-string-parser": true,
+ "@babel/code-frame>@babel/helper-validator-identifier": true
+ }
+ },
"sass-embedded>@bufbuild/protobuf": {
"globals": {
"TextDecoder": true,
@@ -881,7 +908,7 @@
"eslint-plugin-prettier": true,
"eslint-plugin-react": true,
"eslint-plugin-react-hooks": true,
- "eslint>globals": true,
+ "eslint>@eslint/eslintrc>globals": true,
"eslint>ignore": true,
"eslint>minimatch": true,
"mocha>strip-json-comments": true
@@ -914,6 +941,16 @@
"eslint>minimatch": true
}
},
+ "@babel/core>@ampproject/remapping>@jridgewell/gen-mapping": {
+ "globals": {
+ "define": true
+ },
+ "packages": {
+ "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/set-array": true,
+ "terser-webpack-plugin>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": true,
+ "terser-webpack-plugin>@jridgewell/trace-mapping": true
+ }
+ },
"terser>@jridgewell/source-map>@jridgewell/gen-mapping": {
"globals": {
"define": true
@@ -975,7 +1012,7 @@
"node:path.relative": true
},
"packages": {
- "depcheck>resolve": true
+ "lavamoat>@lavamoat/aa>resolve": true
}
},
"@lavamoat/lavapack": {
@@ -1376,7 +1413,7 @@
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>define-properties": true,
"string.prototype.matchall>es-abstract": true,
- "string.prototype.matchall>get-intrinsic": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
"eslint-plugin-react>array-includes>is-string": true
}
},
@@ -1452,7 +1489,7 @@
},
"packages": {
"browserslist": true,
- "autoprefixer>caniuse-lite": true,
+ "browserslist>caniuse-lite": true,
"autoprefixer>normalize-range": true,
"stylelint>autoprefixer>num2fraction": true,
"stylelint>postcss>picocolors": true,
@@ -1462,7 +1499,7 @@
},
"@babel/preset-env>babel-plugin-polyfill-corejs2": {
"packages": {
- "@babel/preset-env>@babel/compat-data": true,
+ "@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/compat-data": true,
"@babel/core": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/helper-define-polyfill-provider": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>semver": true
@@ -1559,6 +1596,9 @@
}
},
"chokidar>braces": {
+ "globals": {
+ "console.log": true
+ },
"packages": {
"chokidar>braces>fill-range": true
}
@@ -1614,7 +1654,7 @@
"process.platform": true
},
"packages": {
- "depcheck>resolve": true
+ "browserify>resolve": true
}
},
"browserify": {
@@ -1647,7 +1687,7 @@
"labeled-stream-splicer": true,
"browserify>module-deps": true,
"browserify>read-only-stream": true,
- "depcheck>resolve": true,
+ "browserify>resolve": true,
"browserify>shasum-object": true,
"browserify>syntax-error": true,
"browserify>through2": true,
@@ -1671,7 +1711,7 @@
"process.versions.node": true
},
"packages": {
- "autoprefixer>caniuse-lite": true,
+ "browserslist>caniuse-lite": true,
"browserslist>electron-to-chromium": true,
"browserslist>node-releases": true
}
@@ -1717,15 +1757,44 @@
"process.cwd": true
}
},
- "string.prototype.matchall>call-bind": {
+ "string.prototype.matchall>call-bind>call-bind-apply-helpers": {
+ "packages": {
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "browserify>has>function-bind": true
+ }
+ },
+ "string.prototype.matchall>regexp.prototype.flags>call-bind": {
"packages": {
"string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"browserify>has>function-bind": true,
- "string.prototype.matchall>get-intrinsic": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
"string.prototype.matchall>call-bind>set-function-length": true
}
},
+ "string.prototype.matchall>es-abstract>safe-regex-test>call-bind": {
+ "packages": {
+ "string.prototype.matchall>call-bind>es-define-property": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "browserify>has>function-bind": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>call-bind>set-function-length": true
+ }
+ },
+ "string.prototype.matchall>call-bind": {
+ "packages": {
+ "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
+ "string.prototype.matchall>call-bind>es-define-property": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>call-bind>set-function-length": true
+ }
+ },
+ "gulp>vinyl-fs>object.assign>call-bound": {
+ "packages": {
+ "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true
+ }
+ },
"chalk": {
"packages": {
"chalk>ansi-styles": true,
@@ -1795,6 +1864,7 @@
"chokidar>anymatch": true,
"chokidar>braces": true,
"chokidar>fsevents": true,
+ "tsx>fsevents": true,
"eslint>glob-parent": true,
"chokidar>is-binary-path": true,
"del>is-glob": true,
@@ -2196,7 +2266,7 @@
"process": true
},
"packages": {
- "nock>debug>ms": true,
+ "mocha>ms": true,
"mocha>supports-color": true
}
},
@@ -2245,7 +2315,7 @@
"string.prototype.matchall>define-properties>define-data-property": {
"packages": {
"string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"string.prototype.matchall>es-abstract>gopd": true
}
},
@@ -2401,6 +2471,12 @@
"stylelint>postcss-html>htmlparser2>domelementtype": true
}
},
+ "eslint-plugin-react>array-includes>get-intrinsic>get-proto>dunder-proto": {
+ "packages": {
+ "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
+ "string.prototype.matchall>es-abstract>gopd": true
+ }
+ },
"browserify>duplexer2": {
"packages": {
"browserify>duplexer2>readable-stream": true
@@ -2476,11 +2552,11 @@
"packages": {
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"string.prototype.matchall>es-abstract>es-object-atoms": true,
"string.prototype.matchall>es-abstract>es-set-tostringtag": true,
"string.prototype.matchall>es-abstract>es-to-primitive": true,
- "string.prototype.matchall>get-intrinsic": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
"string.prototype.matchall>es-abstract>gopd": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true,
"string.prototype.matchall>es-abstract>has-proto": true,
@@ -2495,19 +2571,15 @@
"string.prototype.matchall>es-abstract>string.prototype.trim": true
}
},
- "string.prototype.matchall>call-bind>es-define-property": {
- "packages": {
- "string.prototype.matchall>get-intrinsic": true
- }
- },
"string.prototype.matchall>es-abstract>es-object-atoms": {
"packages": {
- "string.prototype.matchall>call-bind>es-errors": true
+ "string.prototype.matchall>es-abstract>es-errors": true
}
},
"string.prototype.matchall>es-abstract>es-set-tostringtag": {
"packages": {
- "string.prototype.matchall>get-intrinsic": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
"koa>is-generator-function>has-tostringtag": true,
"depcheck>is-core-module>hasown": true
}
@@ -2520,7 +2592,7 @@
"string.prototype.matchall>es-abstract>es-to-primitive": {
"packages": {
"string.prototype.matchall>es-abstract>is-callable": true,
- "@metamask/eth-token-tracker>deep-equal>is-date-object": true,
+ "string.prototype.matchall>es-abstract>es-to-primitive>is-date-object": true,
"string.prototype.matchall>es-abstract>es-to-primitive>is-symbol": true
}
},
@@ -2638,7 +2710,8 @@
},
"packages": {
"eslint-import-resolver-node>debug": true,
- "depcheck>resolve": true
+ "depcheck>is-core-module": true,
+ "browserify>resolve": true
}
},
"eslint-import-resolver-typescript": {
@@ -2653,8 +2726,8 @@
"nock>debug": true,
"nyc>glob": true,
"del>is-glob": true,
- "depcheck>resolve": true,
- "eslint-plugin-import>tsconfig-paths": true
+ "browserify>resolve": true,
+ "eslint-import-resolver-typescript>tsconfig-paths": true
}
},
"eslint-plugin-import>eslint-module-utils": {
@@ -2764,7 +2837,7 @@
"eslint-plugin-node>eslint-utils": true,
"eslint>ignore": true,
"eslint>minimatch": true,
- "depcheck>resolve": true,
+ "browserify>resolve": true,
"eslint-plugin-node>semver": true
}
},
@@ -3364,6 +3437,13 @@
"gulp-watch>chokidar>fsevents>node-pre-gyp": true
}
},
+ "tsx>fsevents": {
+ "globals": {
+ "console.assert": true,
+ "process.platform": true
+ },
+ "native": true
+ },
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge": {
"builtin": {
"util.format": true
@@ -3390,18 +3470,34 @@
"assert.equal": true
}
},
- "string.prototype.matchall>get-intrinsic": {
+ "eslint-plugin-react>array-includes>get-intrinsic": {
"globals": {
"AggregateError": true,
"FinalizationRegistry": true,
"WeakRef": true
},
"packages": {
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
+ "string.prototype.matchall>call-bind>es-define-property": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-object-atoms": true,
"browserify>has>function-bind": true,
- "string.prototype.matchall>es-abstract>has-proto": true,
+ "eslint-plugin-react>array-includes>get-intrinsic>get-proto": true,
+ "string.prototype.matchall>es-abstract>gopd": true,
"string.prototype.matchall>has-symbols": true,
- "depcheck>is-core-module>hasown": true
+ "depcheck>is-core-module>hasown": true,
+ "eslint-plugin-react>array-includes>get-intrinsic>math-intrinsics": true
+ }
+ },
+ "string.prototype.matchall>get-intrinsic": {
+ "packages": {
+ "string.prototype.matchall>has-symbols": true
+ }
+ },
+ "eslint-plugin-react>array-includes>get-intrinsic>get-proto": {
+ "packages": {
+ "eslint-plugin-react>array-includes>get-intrinsic>get-proto>dunder-proto": true,
+ "string.prototype.matchall>es-abstract>es-object-atoms": true
}
},
"gulp-zip>get-stream": {
@@ -3560,11 +3656,6 @@
"define": true
}
},
- "string.prototype.matchall>es-abstract>gopd": {
- "packages": {
- "string.prototype.matchall>get-intrinsic": true
- }
- },
"del>graceful-fs": {
"builtin": {
"assert.equal": true,
@@ -3910,7 +4001,7 @@
},
"string.prototype.matchall>internal-slot": {
"packages": {
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"depcheck>is-core-module>hasown": true,
"string.prototype.matchall>side-channel": true
}
@@ -3963,6 +4054,14 @@
"depcheck>is-core-module>hasown": true
}
},
+ "browserify>resolve>is-core-module": {
+ "globals": {
+ "process.versions": true
+ },
+ "packages": {
+ "depcheck>is-core-module>hasown": true
+ }
+ },
"gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": {
"packages": {
"gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": true
@@ -3978,7 +4077,7 @@
"gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
}
},
- "@metamask/eth-token-tracker>deep-equal>is-date-object": {
+ "string.prototype.matchall>es-abstract>es-to-primitive>is-date-object": {
"packages": {
"koa>is-generator-function>has-tostringtag": true
}
@@ -4082,8 +4181,10 @@
},
"string.prototype.matchall>es-abstract>is-regex": {
"packages": {
- "string.prototype.matchall>call-bind": true,
- "koa>is-generator-function>has-tostringtag": true
+ "gulp>vinyl-fs>object.assign>call-bound": true,
+ "string.prototype.matchall>es-abstract>gopd": true,
+ "koa>is-generator-function>has-tostringtag": true,
+ "depcheck>is-core-module>hasown": true
}
},
"gulp>gulp-cli>replace-homedir>is-absolute>is-relative": {
@@ -4093,12 +4194,13 @@
},
"eslint-plugin-react>array-includes>is-string": {
"packages": {
+ "gulp>vinyl-fs>object.assign>call-bound": true,
"koa>is-generator-function>has-tostringtag": true
}
},
"string.prototype.matchall>es-abstract>es-to-primitive>is-symbol": {
"packages": {
- "string.prototype.matchall>has-symbols": true
+ "string.prototype.matchall>es-abstract>es-to-primitive>is-symbol>has-symbols": true
}
},
"gulp>gulp-cli>replace-homedir>is-absolute>is-relative>is-unc-path": {
@@ -4178,6 +4280,11 @@
"console.warn": true
}
},
+ "eslint-import-resolver-typescript>tsconfig-paths>json5": {
+ "globals": {
+ "console.warn": true
+ }
+ },
"eslint-plugin-import>tsconfig-paths>json5": {
"globals": {
"console.warn": true
@@ -4204,7 +4311,7 @@
"console.error": true
},
"packages": {
- "gulp>vinyl-fs>object.assign": true
+ "eslint-plugin-react>jsx-ast-utils>object.assign": true
}
},
"gulp>glob-watcher>just-debounce": {
@@ -4348,8 +4455,8 @@
"console.log": true
},
"packages": {
- "@babel/core>@babel/parser": true,
- "depcheck>@babel/traverse": true
+ "lavamoat>lavamoat-tofu>@babel/parser": true,
+ "lavamoat>lavamoat-tofu>@babel/traverse": true
}
},
"lavamoat>lavamoat-core>merge-deep>clone-deep>lazy-cache": {
@@ -4651,7 +4758,7 @@
"loose-envify": true,
"browserify>parents": true,
"browserify>module-deps>readable-stream": true,
- "depcheck>resolve": true,
+ "browserify>resolve": true,
"browserify>module-deps>stream-combiner2": true,
"browserify>module-deps>through2": true,
"watchify>xtend": true
@@ -4788,15 +4895,34 @@
"WeakRef": true
}
},
+ "string.prototype.matchall>side-channel>object-inspect": {
+ "builtin": {
+ "util.inspect": true
+ },
+ "globals": {
+ "HTMLElement": true,
+ "WeakRef": true
+ }
+ },
"gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>object-visit": {
"packages": {
"gulp>gulp-cli>isobject": true
}
},
+ "eslint-plugin-react>jsx-ast-utils>object.assign": {
+ "packages": {
+ "string.prototype.matchall>call-bind": true,
+ "string.prototype.matchall>define-properties": true,
+ "string.prototype.matchall>has-symbols": true,
+ "@lavamoat/lavapack>json-stable-stringify>object-keys": true
+ }
+ },
"gulp>vinyl-fs>object.assign": {
"packages": {
"string.prototype.matchall>call-bind": true,
+ "gulp>vinyl-fs>object.assign>call-bound": true,
"string.prototype.matchall>define-properties": true,
+ "string.prototype.matchall>es-abstract>es-object-atoms": true,
"string.prototype.matchall>has-symbols": true,
"@lavamoat/lavapack>json-stable-stringify>object-keys": true
}
@@ -7065,9 +7191,9 @@
},
"string.prototype.matchall>regexp.prototype.flags": {
"packages": {
- "string.prototype.matchall>call-bind": true,
+ "string.prototype.matchall>regexp.prototype.flags>call-bind": true,
"string.prototype.matchall>define-properties": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"string.prototype.matchall>regexp.prototype.flags>set-function-name": true
}
},
@@ -7222,7 +7348,37 @@
"gulp>vinyl-fs>value-or-function": true
}
},
- "depcheck>resolve": {
+ "lavamoat>@lavamoat/aa>resolve": {
+ "builtin": {
+ "fs.readFileSync": true,
+ "fs.realpathSync": true,
+ "fs.statSync": true,
+ "os.homedir": true,
+ "path.dirname": true,
+ "path.join": true,
+ "path.parse": true,
+ "path.relative": true,
+ "path.resolve": true
+ },
+ "globals": {
+ "process.env.HOME": true,
+ "process.env.HOMEDRIVE": true,
+ "process.env.HOMEPATH": true,
+ "process.env.LNAME": true,
+ "process.env.LOGNAME": true,
+ "process.env.USER": true,
+ "process.env.USERNAME": true,
+ "process.env.USERPROFILE": true,
+ "process.getuid": true,
+ "process.platform": true,
+ "process.versions.pnp": true
+ },
+ "packages": {
+ "depcheck>is-core-module": true,
+ "depcheck>resolve>path-parse": true
+ }
+ },
+ "browserify>resolve": {
"builtin": {
"fs.readFile": true,
"fs.readFileSync": true,
@@ -7252,7 +7408,7 @@
"process.versions.pnp": true
},
"packages": {
- "depcheck>is-core-module": true,
+ "browserify>resolve>is-core-module": true,
"depcheck>resolve>path-parse": true
}
},
@@ -7510,8 +7666,8 @@
},
"string.prototype.matchall>es-abstract>safe-regex-test": {
"packages": {
- "string.prototype.matchall>call-bind": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>safe-regex-test>call-bind": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"string.prototype.matchall>es-abstract>is-regex": true
}
},
@@ -7651,8 +7807,8 @@
"string.prototype.matchall>call-bind>set-function-length": {
"packages": {
"string.prototype.matchall>define-properties>define-data-property": true,
- "string.prototype.matchall>call-bind>es-errors": true,
- "string.prototype.matchall>get-intrinsic": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
"string.prototype.matchall>es-abstract>gopd": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true
}
@@ -7660,7 +7816,7 @@
"string.prototype.matchall>regexp.prototype.flags>set-function-name": {
"packages": {
"string.prototype.matchall>define-properties>define-data-property": true,
- "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
"string.prototype.matchall>es-abstract>function.prototype.name>functions-have-names": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true
}
@@ -7692,11 +7848,36 @@
"@metamask/rpc-errors>fast-safe-stringify": true
}
},
+ "string.prototype.matchall>side-channel>side-channel-list": {
+ "packages": {
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>side-channel>object-inspect": true
+ }
+ },
+ "string.prototype.matchall>side-channel>side-channel-map": {
+ "packages": {
+ "gulp>vinyl-fs>object.assign>call-bound": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>side-channel>object-inspect": true
+ }
+ },
+ "string.prototype.matchall>side-channel>side-channel-weakmap": {
+ "packages": {
+ "gulp>vinyl-fs>object.assign>call-bound": true,
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>side-channel>object-inspect": true,
+ "string.prototype.matchall>side-channel>side-channel-map": true
+ }
+ },
"string.prototype.matchall>side-channel": {
"packages": {
- "string.prototype.matchall>call-bind": true,
- "string.prototype.matchall>get-intrinsic": true,
- "string.prototype.matchall>es-abstract>object-inspect": true
+ "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>side-channel>object-inspect": true,
+ "string.prototype.matchall>side-channel>side-channel-list": true,
+ "string.prototype.matchall>side-channel>side-channel-map": true,
+ "string.prototype.matchall>side-channel>side-channel-weakmap": true
}
},
"nyc>signal-exit": {
@@ -8642,7 +8823,7 @@
"ts-node>yn": true
}
},
- "eslint-plugin-import>tsconfig-paths": {
+ "eslint-import-resolver-typescript>tsconfig-paths": {
"builtin": {
"fs.existsSync": true,
"fs.lstatSync": true,
@@ -8664,11 +8845,26 @@
"process.env": true
},
"packages": {
- "eslint-plugin-import>tsconfig-paths>json5": true,
+ "eslint-import-resolver-typescript>tsconfig-paths>json5": true,
"wait-on>minimist": true,
"eslint-plugin-import>tsconfig-paths>strip-bom": true
}
},
+ "eslint-plugin-import>tsconfig-paths": {
+ "builtin": {
+ "fs.existsSync": true,
+ "fs.lstatSync": true,
+ "fs.readFileSync": true,
+ "fs.statSync": true,
+ "path.dirname": true,
+ "path.join": true,
+ "path.resolve": true
+ },
+ "packages": {
+ "eslint-plugin-import>tsconfig-paths>json5": true,
+ "eslint-plugin-import>tsconfig-paths>strip-bom": true
+ }
+ },
"tsutils": {
"packages": {
"tslib": true,
diff --git a/manifest-flags.json b/manifest-flags.json
new file mode 100644
index 000000000000..f90de282f373
--- /dev/null
+++ b/manifest-flags.json
@@ -0,0 +1,8 @@
+{
+ "remoteFeatureFlags": {
+ "testFlagForThreshold": {
+ "name": "test-flag",
+ "value": "121212"
+ }
+ }
+}
diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts
index 2826200dc293..42a4e0fc3f12 100644
--- a/test/e2e/constants.ts
+++ b/test/e2e/constants.ts
@@ -87,3 +87,21 @@ export enum ACCOUNT_TYPE {
/* Meta metricsId generated by generateMetaMetricsId */
export const MOCK_META_METRICS_ID =
'0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420';
+
+/* Mock remote feature flags response */
+export const MOCK_REMOTE_FEATURE_FLAGS_RESPONSE = {
+ feature1: true,
+ feature2: false,
+ feature3: {
+ name: 'groupC',
+ value: 'valueC',
+ },
+};
+
+/* Mock customized remote feature flags response */
+export const MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS = {
+ feature1: {
+ name: 'groupA',
+ value: 'valueA',
+ },
+};
diff --git a/test/e2e/page-objects/pages/developer-options-page.ts b/test/e2e/page-objects/pages/developer-options-page.ts
index c15f6c767a82..8d37fc4091f3 100644
--- a/test/e2e/page-objects/pages/developer-options-page.ts
+++ b/test/e2e/page-objects/pages/developer-options-page.ts
@@ -1,4 +1,6 @@
+import { strict as assert } from 'assert';
import { Driver } from '../../webdriver/driver';
+import { MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS } from '../../constants';
class DevelopOptions {
private readonly driver: Driver;
@@ -12,6 +14,9 @@ class DevelopOptions {
css: 'h4',
};
+ private readonly developerOptionsRemoteFeatureFlagsState: string =
+ '[data-testid="developer-options-remote-feature-flags"]';
+
constructor(driver: Driver) {
this.driver = driver;
}
@@ -33,6 +38,18 @@ class DevelopOptions {
console.log('Generate a page crash in Developer option page');
await this.driver.clickElement(this.generatePageCrashButton);
}
+
+ async validateRemoteFeatureFlagState(): Promise {
+ console.log('Validate remote feature flags state in Developer option page');
+ const element = await this.driver.findElement(
+ this.developerOptionsRemoteFeatureFlagsState,
+ );
+ const remoteFeatureFlagsState = await element.getText();
+ assert.equal(
+ remoteFeatureFlagsState,
+ JSON.stringify(MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS),
+ );
+ }
}
export default DevelopOptions;
diff --git a/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts b/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
index 42ebe7dc762c..48c8dd7f5b28 100644
--- a/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
+++ b/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
@@ -4,8 +4,14 @@ import { getCleanAppState, withFixtures } from '../../helpers';
import FixtureBuilder from '../../fixture-builder';
import { TestSuiteArguments } from '../confirmations/transactions/shared';
import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow';
-import { MOCK_META_METRICS_ID } from '../../constants';
-import { MOCK_REMOTE_FEATURE_FLAGS_RESPONSE } from './mock-data';
+import HeaderNavbar from '../../page-objects/pages/header-navbar';
+import SettingsPage from '../../page-objects/pages/settings/settings-page';
+import DevelopOptions from '../../page-objects/pages/developer-options-page';
+import {
+ MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS,
+ MOCK_META_METRICS_ID,
+ MOCK_REMOTE_FEATURE_FLAGS_RESPONSE,
+} from '../../constants';
describe('Remote feature flag', function (this: Suite) {
it('should be fetched with threshold value when basic functionality toggle is on', async function () {
@@ -45,4 +51,33 @@ describe('Remote feature flag', function (this: Suite) {
},
);
});
+
+ it('offers the option to pass into manifest file for developers', async function () {
+ await withFixtures(
+ {
+ fixtures: new FixtureBuilder()
+ .withMetaMetricsController({
+ metaMetricsId: MOCK_META_METRICS_ID,
+ participateInMetaMetrics: true,
+ })
+ .build(),
+ manifestFlags: {
+ remoteFeatureFlags: MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS,
+ },
+ title: this.test?.fullTitle(),
+ },
+ async ({ driver }: TestSuiteArguments) => {
+ await loginWithBalanceValidation(driver);
+ const headerNavbar = new HeaderNavbar(driver);
+ await headerNavbar.openSettingsPage();
+ const settingsPage = new SettingsPage(driver);
+ await settingsPage.check_pageIsLoaded();
+ await settingsPage.goToDevelopOptionSettings();
+
+ const developOptionsPage = new DevelopOptions(driver);
+ await developOptionsPage.check_pageIsLoaded();
+ await developOptionsPage.validateRemoteFeatureFlagState();
+ },
+ );
+ });
});
diff --git a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap
index 6c1fae43d5e8..baba3f1ffc70 100644
--- a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap
+++ b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap
@@ -10,6 +10,45 @@ exports[`Develop options tab should match snapshot 1`] = `
>
States
+
+ Current States
+
+
+
+
+
+ Remote feature flags
+
+
+ The remote feature flags here by
+
+ getRemoteFeatureFlags()
+
+ is retrieved from one of the following sources:
+
+ 1) manifest-flags.json file 2) RemoteFeatureFlagsController
+
+ Modify the manifest-flags.json file will change the state locally.
+
+
+
+ {"feature1":"value1"}
+
+
+
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
index ff76752aed46..5f97bc987323 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
@@ -6,8 +6,11 @@ import { renderWithProvider } from '../../../../test/jest/rendering';
import mockState from '../../../../test/data/mock-state.json';
import DeveloperOptionsTab from '.';
-const mockSetServiceWorkerKeepAlivePreference = jest.fn();
-
+const mockSetServiceWorkerKeepAlivePreference = jest.fn().mockReturnValue({
+ type: 'SET_SERVICE_WORKER_KEEP_ALIVE',
+ value: true,
+});
+const mockRemoteFeatureFlags = { feature1: 'value1' };
// eslint-disable-next-line
/* @ts-expect-error: Avoids error from window property not existing */
window.metamaskFeatureFlags = {};
@@ -17,16 +20,24 @@ jest.mock('../../../store/actions.ts', () => ({
mockSetServiceWorkerKeepAlivePreference,
}));
+jest.mock('../../../selectors', () => ({
+ ...jest.requireActual('../../../selectors'),
+ getRemoteFeatureFlags: jest.fn(() => mockRemoteFeatureFlags),
+}));
+
describe('Develop options tab', () => {
const mockStore = configureMockStore([thunk])(mockState);
it('should match snapshot', () => {
- const { container } = renderWithProvider(
+ const { getByTestId, container } = renderWithProvider(
,
mockStore,
);
expect(container).toMatchSnapshot();
+ expect(
+ getByTestId('developer-options-remote-feature-flags').textContent,
+ ).toEqual(JSON.stringify(mockRemoteFeatureFlags));
});
it('should toggle Service Worker Keep Alive', async () => {
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
index a604082586fc..866e711d821c 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
@@ -37,6 +37,7 @@ import {
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { getIsRedesignedConfirmationsDeveloperEnabled } from '../../confirmations/selectors/confirm';
+import { getRemoteFeatureFlags } from '../../../selectors';
import ToggleRow from './developer-options-toggle-row-component';
import SentryTest from './sentry-test';
import { ProfileSyncDevSettings } from './profile-sync';
@@ -241,11 +242,54 @@ const DeveloperOptionsTab = () => {
);
};
+ const renderRemoteFeatureFlags = () => {
+ const remoteFeatureFlags = useSelector(getRemoteFeatureFlags);
+ return (
+
+
+
Remote feature flags
+
+ The remote feature flags here by getRemoteFeatureFlags() is
+ retrieved from one of the following sources:
+
+ 1) manifest-flags.json file 2) RemoteFeatureFlagsController
+
+ Modify the manifest-flags.json file will change the state locally.
+
+
+
+ {JSON.stringify(remoteFeatureFlags)}
+
+
+ );
+ };
+
return (
States
+
+
}
+ >
+ Current States
+
+
+ {renderRemoteFeatureFlags()}
+
{
>
Reset States
-
{renderAnnouncementReset()}
{renderOnboardingReset()}
diff --git a/ui/pages/settings/info-tab/info-tab.component.js b/ui/pages/settings/info-tab/info-tab.component.js
index 6da93878c7ef..121f29e7c52c 100644
--- a/ui/pages/settings/info-tab/info-tab.component.js
+++ b/ui/pages/settings/info-tab/info-tab.component.js
@@ -28,10 +28,6 @@ import {
} from '../../../../shared/lib/ui-utils';
export default class InfoTab extends PureComponent {
- static propTypes = {
- remoteFeatureFlags: PropTypes.object.isRequired,
- };
-
state = {
version: process.env.METAMASK_VERSION,
};
@@ -57,14 +53,6 @@ export default class InfoTab extends PureComponent {
componentDidMount() {
const { t } = this.context;
handleSettingsRefs(t, t('about'), this.settingsRefs);
- if (this.props.remoteFeatureFlags.testFlagForThreshold) {
- // eslint-disable-next-line no-console
- console.log(
- `Fetch remote feature flag success, eg: testFlagForThreshold has value ${JSON.stringify(
- this.props.remoteFeatureFlags.testFlagForThreshold,
- )}`,
- );
- }
}
renderInfoLinks() {
diff --git a/ui/pages/settings/info-tab/info-tab.stories.js b/ui/pages/settings/info-tab/info-tab.stories.js
index 020f60d2516e..34f855172d1c 100644
--- a/ui/pages/settings/info-tab/info-tab.stories.js
+++ b/ui/pages/settings/info-tab/info-tab.stories.js
@@ -5,6 +5,6 @@ export default {
title: 'Pages/Settings/InfoTab',
};
-export const DefaultStory = () => ;
+export const DefaultStory = () => ;
DefaultStory.storyName = 'Default';
diff --git a/ui/pages/settings/info-tab/info-tab.test.tsx b/ui/pages/settings/info-tab/info-tab.test.tsx
index 7625caf09efb..b25be3f8ed5e 100644
--- a/ui/pages/settings/info-tab/info-tab.test.tsx
+++ b/ui/pages/settings/info-tab/info-tab.test.tsx
@@ -7,9 +7,7 @@ describe('InfoTab', () => {
let getByText: (text: string) => HTMLElement;
beforeEach(() => {
- const renderResult = renderWithProvider(
- ,
- );
+ const renderResult = renderWithProvider();
getByText = renderResult.getByText;
});
diff --git a/ui/pages/settings/settings.component.js b/ui/pages/settings/settings.component.js
index 37257e2c8fcb..cd9307013a68 100644
--- a/ui/pages/settings/settings.component.js
+++ b/ui/pages/settings/settings.component.js
@@ -73,7 +73,6 @@ class SettingsPage extends PureComponent {
isPopup: PropTypes.bool,
mostRecentOverviewPage: PropTypes.string.isRequired,
pathnameI18nKey: PropTypes.string,
- remoteFeatureFlags: PropTypes.object.isRequired,
settingsPageSnaps: PropTypes.array,
snapSettingsTitle: PropTypes.string,
toggleNetworkMenu: PropTypes.func.isRequired,
@@ -410,13 +409,7 @@ class SettingsPage extends PureComponent {
/>
)}
/>
- (
-
- )}
- />
+ } />
{
const {
metamask: { currencyRates },
} = state;
- const remoteFeatureFlags = getRemoteFeatureFlags(state);
-
const settingsPageSnapsIds = getSettingsPageSnapsIds(state);
const snapsMetadata = getSnapsMetadata(state);
const conversionDate = currencyRates[ticker]?.conversionDate;
@@ -133,7 +130,6 @@ const mapStateToProps = (state, ownProps) => {
isPopup,
mostRecentOverviewPage: getMostRecentOverviewPage(state),
pathnameI18nKey,
- remoteFeatureFlags,
settingsPageSnaps,
snapSettingsTitle,
useExternalServices,
diff --git a/ui/pages/settings/settings.stories.js b/ui/pages/settings/settings.stories.js
index b6b695a89cb1..e56f679eda5b 100644
--- a/ui/pages/settings/settings.stories.js
+++ b/ui/pages/settings/settings.stories.js
@@ -61,7 +61,6 @@ const Settings = ({ history }) => {
history={history}
pathnameI18nKey={pathnameI18nKey}
backRoute={SETTINGS_ROUTE}
- remoteFeatureFlags={{}}
settingsPageSnaps={[]}
/>
diff --git a/ui/pages/settings/settings.test.js b/ui/pages/settings/settings.test.js
index 061175865fce..238b50f3ce5b 100644
--- a/ui/pages/settings/settings.test.js
+++ b/ui/pages/settings/settings.test.js
@@ -19,7 +19,6 @@ describe('SettingsPage', () => {
isSnapViewPage: false,
mostRecentOverviewPage: '/',
pathnameI18nKey: '',
- remoteFeatureFlags: {},
};
const mockStore = configureMockStore()(mockState);
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 61f8c0ffa034..fa6eb97dd0b2 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -113,6 +113,7 @@ import { hasTransactionData } from '../../shared/modules/transaction.utils';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { createDeepEqualSelector } from '../../shared/modules/selectors/util';
import { isSnapIgnoredInProd } from '../helpers/utils/snaps';
+import { getManifestFlags } from '../../app/scripts/lib/manifestFlags';
import {
getAllUnapprovedTransactions,
getCurrentNetworkTransactions,
@@ -2977,7 +2978,23 @@ export function getMetaMetricsDataDeletionStatus(state) {
return state.metamask.metaMetricsDataDeletionStatus;
}
+/**
+ * Gets the remote feature flags from either the manifest or state.
+ * First checks if remote feature flags exist in the manifest and returns those if present.
+ * Otherwise returns the remote feature flags from the MetaMask state that's retrieved from controller.
+ *
+ * @param {object} state - The MetaMask state object
+ * @returns {object} The remote feature flags object containing feature flag key-value pairs
+ */
export function getRemoteFeatureFlags(state) {
+ const remoteFeatureFlagsValueFromManifest =
+ getManifestFlags().remoteFeatureFlags;
+ if (
+ remoteFeatureFlagsValueFromManifest &&
+ Object.keys(remoteFeatureFlagsValueFromManifest).length > 0
+ ) {
+ return remoteFeatureFlagsValueFromManifest;
+ }
return state.metamask.remoteFeatureFlags;
}
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index 143f0b9e3d74..e1c131d08ca0 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -15,6 +15,7 @@ import { DeleteRegulationStatus } from '../../shared/constants/metametrics';
import { selectSwitchedNetworkNeverShowMessage } from '../components/app/toast-master/selectors';
import * as networkSelectors from '../../shared/modules/selectors/networks';
import * as selectors from './selectors';
+import * as manifestFlags from '../../app/scripts/lib/manifestFlags';
jest.mock('../../shared/modules/selectors/networks', () => ({
...jest.requireActual('../../shared/modules/selectors/networks'),
@@ -2155,16 +2156,71 @@ describe('#getConnectedSitesList', () => {
});
describe('#getRemoteFeatureFlags', () => {
- it('returns remoteFeatureFlags in state', () => {
+ let getManifestFlagsMock;
+
+ beforeEach(() => {
+ // Mock the getManifestFlags function before each test
+ getManifestFlagsMock = jest.spyOn(manifestFlags, 'getManifestFlags').mockReturnValue({});
+ });
+
+ afterEach(() => {
+ // Clean up mock after each test
+ getManifestFlagsMock.mockRestore();
+ });
+
+ it('returns manifest flags when they are provided in manifest-flags.json', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {
+ manifestFlag1: true,
+ manifestFlag2: false,
+ },
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {
+ stateFlag: true,
+ },
+ },
+ };
+
+ expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
+ manifestFlag1: true,
+ manifestFlag2: false,
+ });
+ });
+
+ it('returns state flags when manifest flags are empty', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {},
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {
+ stateFlag: true,
+ },
+ },
+ };
+
+ expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
+ stateFlag: true,
+ });
+ });
+
+ it('returns state flags when manifest flags are undefined', () => {
+ getManifestFlagsMock.mockReturnValue({});
+
const state = {
metamask: {
remoteFeatureFlags: {
- existingFlag: true,
+ stateFlag: true,
},
},
};
+
expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
- existingFlag: true,
+ stateFlag: true,
});
});
});
From 887ed35ab17653c3f11a42004d18a30687190c68 Mon Sep 17 00:00:00 2001
From: MetaMask Bot
Date: Tue, 14 Jan 2025 15:12:22 +0000
Subject: [PATCH 02/20] Update LavaMoat policies
---
.../webpack/test/webpack.config.test.ts | 6 +-
.../utils/plugins/ManifestPlugin/helpers.ts | 5 +-
lavamoat/browserify/beta/policy.json | 2 +-
lavamoat/browserify/flask/policy.json | 2 +-
lavamoat/browserify/main/policy.json | 2 +-
lavamoat/browserify/mmi/policy.json | 2 +-
lavamoat/build-system/policy.json | 346 ++++--------------
ui/selectors/selectors.js | 1 +
ui/selectors/selectors.test.js | 7 +-
9 files changed, 97 insertions(+), 276 deletions(-)
diff --git a/development/webpack/test/webpack.config.test.ts b/development/webpack/test/webpack.config.test.ts
index e1d11f953829..796a2233bdd3 100644
--- a/development/webpack/test/webpack.config.test.ts
+++ b/development/webpack/test/webpack.config.test.ts
@@ -78,7 +78,7 @@ ${Object.entries(env)
}
it('should have the correct defaults', () => {
- const config: Configuration = getWebpackConfig();
+ const config: Configuration = getWebpackConfig(['--test']);
// check that options are valid
const { options } = webpack(config);
assert.strictEqual(options.name, 'MetaMask – development');
@@ -162,6 +162,7 @@ ${Object.entries(env)
{
manifest_version: 3,
name: 'name',
+ permissions: ['tabs'],
version: '1.2.3',
content_scripts: [
{
@@ -191,6 +192,7 @@ ${Object.entries(env)
'--no-progress',
'--no-cache',
'--zip',
+ '--test',
...removeUnsupportedFeatures,
],
{
@@ -231,7 +233,7 @@ ${Object.entries(env)
assert.deepStrictEqual(manifestPlugin.options.description, null);
assert.deepStrictEqual(manifestPlugin.options.zip, true);
assert(manifestPlugin.options.zipOptions, 'Zip options should be present');
- assert.strictEqual(manifestPlugin.options.transform, undefined);
+ assert.notEqual(manifestPlugin.options.transform, undefined);
const progressPlugin = instance.options.plugins.find(
(plugin) => plugin && plugin.constructor.name === 'ProgressPlugin',
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index 1db54118996e..9f8104eb3c0a 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -35,7 +35,10 @@ export function transformManifest(args: { lockdown: boolean; test: boolean }) {
browserManifest._flags = manifestFlags;
}
- transforms.push(addManifestFlags);
+ // Add manifest flags only for non-test builds so the test build is not affected by local feature flags
+ if (!args.test) {
+ transforms.push(addManifestFlags);
+ }
if (!args.lockdown) {
// remove lockdown scripts from content_scripts
diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json
index dc35b61fdd86..c4985af54a1d 100644
--- a/lavamoat/browserify/beta/policy.json
+++ b/lavamoat/browserify/beta/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json
index dc35b61fdd86..c4985af54a1d 100644
--- a/lavamoat/browserify/flask/policy.json
+++ b/lavamoat/browserify/flask/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json
index dc35b61fdd86..c4985af54a1d 100644
--- a/lavamoat/browserify/main/policy.json
+++ b/lavamoat/browserify/main/policy.json
@@ -5775,4 +5775,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json
index c5d67dc34329..bc5be17e5a64 100644
--- a/lavamoat/browserify/mmi/policy.json
+++ b/lavamoat/browserify/mmi/policy.json
@@ -5867,4 +5867,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json
index 7fc9728c7c90..5c9dbd68b17c 100644
--- a/lavamoat/build-system/policy.json
+++ b/lavamoat/build-system/policy.json
@@ -5,7 +5,7 @@
"define": true
},
"packages": {
- "@babel/core>@ampproject/remapping>@jridgewell/gen-mapping": true,
+ "terser>@jridgewell/source-map>@jridgewell/gen-mapping": true,
"terser-webpack-plugin>@jridgewell/trace-mapping": true
}
},
@@ -60,7 +60,7 @@
"@babel/preset-react": true,
"@babel/preset-typescript": true,
"@babel/core>@babel/template": true,
- "@babel/core>@babel/traverse": true,
+ "depcheck>@babel/traverse": true,
"@babel/core>@babel/types": true,
"@babel/core>convert-source-map": true,
"nock>debug": true,
@@ -147,7 +147,7 @@
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true,
"@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true,
- "@babel/core>@babel/traverse": true,
+ "depcheck>@babel/traverse": true,
"@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin>semver": true
}
},
@@ -175,7 +175,7 @@
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/helper-define-polyfill-provider>lodash.debounce": true,
- "browserify>resolve": true
+ "depcheck>resolve": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": {
@@ -200,8 +200,9 @@
"packages": {
"@babel/core": true,
"@babel/core>@babel/helper-module-transforms>@babel/helper-module-imports": true,
+ "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": true,
"@babel/code-frame>@babel/helper-validator-identifier": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": {
@@ -214,7 +215,7 @@
"@babel/core": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true,
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": {
@@ -222,10 +223,10 @@
"@babel/core": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
- "@babel/preset-env>@babel/plugin-transform-modules-commonjs>@babel/helper-simple-access": {
+ "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": {
"packages": {
"@babel/core>@babel/types": true
}
@@ -238,7 +239,7 @@
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": {
"packages": {
"@babel/core>@babel/template": true,
- "@babel/core>@babel/traverse": true,
+ "depcheck>@babel/traverse": true,
"@babel/core>@babel/types": true
}
},
@@ -251,7 +252,7 @@
"@babel/preset-env>@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
"packages": {
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-bugfix-safari-class-field-initializer-scope": {
@@ -277,7 +278,7 @@
"packages": {
"@babel/core": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-syntax-import-assertions": {
@@ -316,7 +317,7 @@
"@babel/core": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-async-to-generator": {
@@ -358,8 +359,8 @@
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true,
- "@babel/core>@babel/traverse": true,
- "depcheck>@babel/traverse>globals": true
+ "depcheck>@babel/traverse": true,
+ "@babel/preset-env>@babel/plugin-transform-classes>globals": true
}
},
"@babel/preset-env>@babel/plugin-transform-computed-properties": {
@@ -422,7 +423,7 @@
"packages": {
"@babel/core>@babel/helper-compilation-targets": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-json-strings": {
@@ -460,7 +461,7 @@
"@babel/core": true,
"@babel/core>@babel/helper-module-transforms": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
- "@babel/preset-env>@babel/plugin-transform-modules-commonjs>@babel/helper-simple-access": true
+ "@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": true
}
},
"@babel/preset-env>@babel/plugin-transform-modules-systemjs": {
@@ -472,7 +473,7 @@
"@babel/core>@babel/helper-module-transforms": true,
"@babel/preset-env>@babel/helper-plugin-utils": true,
"@babel/code-frame>@babel/helper-validator-identifier": true,
- "@babel/core>@babel/traverse": true
+ "depcheck>@babel/traverse": true
}
},
"@babel/preset-env>@babel/plugin-transform-modules-umd": {
@@ -779,7 +780,7 @@
"@babel/core>@babel/types": true
}
},
- "@babel/core>@babel/traverse": {
+ "depcheck>@babel/traverse": {
"globals": {
"console.log": true
},
@@ -789,25 +790,7 @@
"@babel/core>@babel/parser": true,
"@babel/core>@babel/template": true,
"@babel/core>@babel/types": true,
- "nock>debug": true,
- "depcheck>@babel/traverse>globals": true
- }
- },
- "depcheck>@babel/traverse": {
- "packages": {
- "babel/preset-env>b@babel/types": true
- }
- },
- "lavamoat>lavamoat-tofu>@babel/traverse": {
- "globals": {
- "console.log": true
- },
- "packages": {
- "@babel/code-frame": true,
- "@babel/core>@babel/generator": true,
- "lavamoat>lavamoat-tofu>@babel/traverse>@babel/parser": true,
- "@babel/core>@babel/template": true,
- "lavamoat>lavamoat-tofu>@babel/traverse>@babel/types": true,
+ "babel/preset-env>b@babel/types": true,
"nock>debug": true,
"depcheck>@babel/traverse>globals": true
}
@@ -822,16 +805,6 @@
"@babel/code-frame>@babel/helper-validator-identifier": true
}
},
- "lavamoat>lavamoat-tofu>@babel/traverse>@babel/types": {
- "globals": {
- "console.warn": true,
- "process.env": true
- },
- "packages": {
- "@babel/core>@babel/types>@babel/helper-string-parser": true,
- "@babel/code-frame>@babel/helper-validator-identifier": true
- }
- },
"sass-embedded>@bufbuild/protobuf": {
"globals": {
"TextDecoder": true,
@@ -908,7 +881,7 @@
"eslint-plugin-prettier": true,
"eslint-plugin-react": true,
"eslint-plugin-react-hooks": true,
- "eslint>@eslint/eslintrc>globals": true,
+ "eslint>globals": true,
"eslint>ignore": true,
"eslint>minimatch": true,
"mocha>strip-json-comments": true
@@ -941,16 +914,6 @@
"eslint>minimatch": true
}
},
- "@babel/core>@ampproject/remapping>@jridgewell/gen-mapping": {
- "globals": {
- "define": true
- },
- "packages": {
- "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/set-array": true,
- "terser-webpack-plugin>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": true,
- "terser-webpack-plugin>@jridgewell/trace-mapping": true
- }
- },
"terser>@jridgewell/source-map>@jridgewell/gen-mapping": {
"globals": {
"define": true
@@ -1012,7 +975,7 @@
"node:path.relative": true
},
"packages": {
- "lavamoat>@lavamoat/aa>resolve": true
+ "depcheck>resolve": true
}
},
"@lavamoat/lavapack": {
@@ -1413,7 +1376,7 @@
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>define-properties": true,
"string.prototype.matchall>es-abstract": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>get-intrinsic": true,
"eslint-plugin-react>array-includes>is-string": true
}
},
@@ -1489,7 +1452,7 @@
},
"packages": {
"browserslist": true,
- "browserslist>caniuse-lite": true,
+ "autoprefixer>caniuse-lite": true,
"autoprefixer>normalize-range": true,
"stylelint>autoprefixer>num2fraction": true,
"stylelint>postcss>picocolors": true,
@@ -1499,7 +1462,7 @@
},
"@babel/preset-env>babel-plugin-polyfill-corejs2": {
"packages": {
- "@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/compat-data": true,
+ "@babel/preset-env>@babel/compat-data": true,
"@babel/core": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>@babel/helper-define-polyfill-provider": true,
"@babel/preset-env>babel-plugin-polyfill-corejs2>semver": true
@@ -1596,9 +1559,6 @@
}
},
"chokidar>braces": {
- "globals": {
- "console.log": true
- },
"packages": {
"chokidar>braces>fill-range": true
}
@@ -1654,7 +1614,7 @@
"process.platform": true
},
"packages": {
- "browserify>resolve": true
+ "depcheck>resolve": true
}
},
"browserify": {
@@ -1687,7 +1647,7 @@
"labeled-stream-splicer": true,
"browserify>module-deps": true,
"browserify>read-only-stream": true,
- "browserify>resolve": true,
+ "depcheck>resolve": true,
"browserify>shasum-object": true,
"browserify>syntax-error": true,
"browserify>through2": true,
@@ -1711,7 +1671,7 @@
"process.versions.node": true
},
"packages": {
- "browserslist>caniuse-lite": true,
+ "autoprefixer>caniuse-lite": true,
"browserslist>electron-to-chromium": true,
"browserslist>node-releases": true
}
@@ -1757,44 +1717,15 @@
"process.cwd": true
}
},
- "string.prototype.matchall>call-bind>call-bind-apply-helpers": {
- "packages": {
- "string.prototype.matchall>es-abstract>es-errors": true,
- "browserify>has>function-bind": true
- }
- },
- "string.prototype.matchall>regexp.prototype.flags>call-bind": {
- "packages": {
- "string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "browserify>has>function-bind": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
- "string.prototype.matchall>call-bind>set-function-length": true
- }
- },
- "string.prototype.matchall>es-abstract>safe-regex-test>call-bind": {
- "packages": {
- "string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "browserify>has>function-bind": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
- "string.prototype.matchall>call-bind>set-function-length": true
- }
- },
"string.prototype.matchall>call-bind": {
"packages": {
- "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
"string.prototype.matchall>call-bind>es-define-property": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
+ "browserify>has>function-bind": true,
+ "string.prototype.matchall>get-intrinsic": true,
"string.prototype.matchall>call-bind>set-function-length": true
}
},
- "gulp>vinyl-fs>object.assign>call-bound": {
- "packages": {
- "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true
- }
- },
"chalk": {
"packages": {
"chalk>ansi-styles": true,
@@ -2266,7 +2197,7 @@
"process": true
},
"packages": {
- "mocha>ms": true,
+ "nock>debug>ms": true,
"mocha>supports-color": true
}
},
@@ -2315,7 +2246,7 @@
"string.prototype.matchall>define-properties>define-data-property": {
"packages": {
"string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"string.prototype.matchall>es-abstract>gopd": true
}
},
@@ -2471,12 +2402,6 @@
"stylelint>postcss-html>htmlparser2>domelementtype": true
}
},
- "eslint-plugin-react>array-includes>get-intrinsic>get-proto>dunder-proto": {
- "packages": {
- "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
- "string.prototype.matchall>es-abstract>gopd": true
- }
- },
"browserify>duplexer2": {
"packages": {
"browserify>duplexer2>readable-stream": true
@@ -2552,11 +2477,11 @@
"packages": {
"string.prototype.matchall>call-bind": true,
"string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"string.prototype.matchall>es-abstract>es-object-atoms": true,
"string.prototype.matchall>es-abstract>es-set-tostringtag": true,
"string.prototype.matchall>es-abstract>es-to-primitive": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>get-intrinsic": true,
"string.prototype.matchall>es-abstract>gopd": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true,
"string.prototype.matchall>es-abstract>has-proto": true,
@@ -2571,15 +2496,19 @@
"string.prototype.matchall>es-abstract>string.prototype.trim": true
}
},
+ "string.prototype.matchall>call-bind>es-define-property": {
+ "packages": {
+ "string.prototype.matchall>get-intrinsic": true
+ }
+ },
"string.prototype.matchall>es-abstract>es-object-atoms": {
"packages": {
- "string.prototype.matchall>es-abstract>es-errors": true
+ "string.prototype.matchall>call-bind>es-errors": true
}
},
"string.prototype.matchall>es-abstract>es-set-tostringtag": {
"packages": {
- "string.prototype.matchall>es-abstract>es-errors": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>get-intrinsic": true,
"koa>is-generator-function>has-tostringtag": true,
"depcheck>is-core-module>hasown": true
}
@@ -2592,7 +2521,7 @@
"string.prototype.matchall>es-abstract>es-to-primitive": {
"packages": {
"string.prototype.matchall>es-abstract>is-callable": true,
- "string.prototype.matchall>es-abstract>es-to-primitive>is-date-object": true,
+ "@metamask/eth-token-tracker>deep-equal>is-date-object": true,
"string.prototype.matchall>es-abstract>es-to-primitive>is-symbol": true
}
},
@@ -2710,8 +2639,7 @@
},
"packages": {
"eslint-import-resolver-node>debug": true,
- "depcheck>is-core-module": true,
- "browserify>resolve": true
+ "depcheck>resolve": true
}
},
"eslint-import-resolver-typescript": {
@@ -2726,8 +2654,8 @@
"nock>debug": true,
"nyc>glob": true,
"del>is-glob": true,
- "browserify>resolve": true,
- "eslint-import-resolver-typescript>tsconfig-paths": true
+ "depcheck>resolve": true,
+ "eslint-plugin-import>tsconfig-paths": true
}
},
"eslint-plugin-import>eslint-module-utils": {
@@ -2837,7 +2765,7 @@
"eslint-plugin-node>eslint-utils": true,
"eslint>ignore": true,
"eslint>minimatch": true,
- "browserify>resolve": true,
+ "depcheck>resolve": true,
"eslint-plugin-node>semver": true
}
},
@@ -3470,34 +3398,18 @@
"assert.equal": true
}
},
- "eslint-plugin-react>array-includes>get-intrinsic": {
+ "string.prototype.matchall>get-intrinsic": {
"globals": {
"AggregateError": true,
"FinalizationRegistry": true,
"WeakRef": true
},
"packages": {
- "string.prototype.matchall>call-bind>call-bind-apply-helpers": true,
- "string.prototype.matchall>call-bind>es-define-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "string.prototype.matchall>es-abstract>es-object-atoms": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"browserify>has>function-bind": true,
- "eslint-plugin-react>array-includes>get-intrinsic>get-proto": true,
- "string.prototype.matchall>es-abstract>gopd": true,
+ "string.prototype.matchall>es-abstract>has-proto": true,
"string.prototype.matchall>has-symbols": true,
- "depcheck>is-core-module>hasown": true,
- "eslint-plugin-react>array-includes>get-intrinsic>math-intrinsics": true
- }
- },
- "string.prototype.matchall>get-intrinsic": {
- "packages": {
- "string.prototype.matchall>has-symbols": true
- }
- },
- "eslint-plugin-react>array-includes>get-intrinsic>get-proto": {
- "packages": {
- "eslint-plugin-react>array-includes>get-intrinsic>get-proto>dunder-proto": true,
- "string.prototype.matchall>es-abstract>es-object-atoms": true
+ "depcheck>is-core-module>hasown": true
}
},
"gulp-zip>get-stream": {
@@ -3656,6 +3568,11 @@
"define": true
}
},
+ "string.prototype.matchall>es-abstract>gopd": {
+ "packages": {
+ "string.prototype.matchall>get-intrinsic": true
+ }
+ },
"del>graceful-fs": {
"builtin": {
"assert.equal": true,
@@ -4001,7 +3918,7 @@
},
"string.prototype.matchall>internal-slot": {
"packages": {
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"depcheck>is-core-module>hasown": true,
"string.prototype.matchall>side-channel": true
}
@@ -4054,14 +3971,6 @@
"depcheck>is-core-module>hasown": true
}
},
- "browserify>resolve>is-core-module": {
- "globals": {
- "process.versions": true
- },
- "packages": {
- "depcheck>is-core-module>hasown": true
- }
- },
"gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": {
"packages": {
"gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": true
@@ -4077,7 +3986,7 @@
"gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
}
},
- "string.prototype.matchall>es-abstract>es-to-primitive>is-date-object": {
+ "@metamask/eth-token-tracker>deep-equal>is-date-object": {
"packages": {
"koa>is-generator-function>has-tostringtag": true
}
@@ -4181,10 +4090,8 @@
},
"string.prototype.matchall>es-abstract>is-regex": {
"packages": {
- "gulp>vinyl-fs>object.assign>call-bound": true,
- "string.prototype.matchall>es-abstract>gopd": true,
- "koa>is-generator-function>has-tostringtag": true,
- "depcheck>is-core-module>hasown": true
+ "string.prototype.matchall>call-bind": true,
+ "koa>is-generator-function>has-tostringtag": true
}
},
"gulp>gulp-cli>replace-homedir>is-absolute>is-relative": {
@@ -4194,13 +4101,12 @@
},
"eslint-plugin-react>array-includes>is-string": {
"packages": {
- "gulp>vinyl-fs>object.assign>call-bound": true,
"koa>is-generator-function>has-tostringtag": true
}
},
"string.prototype.matchall>es-abstract>es-to-primitive>is-symbol": {
"packages": {
- "string.prototype.matchall>es-abstract>es-to-primitive>is-symbol>has-symbols": true
+ "string.prototype.matchall>has-symbols": true
}
},
"gulp>gulp-cli>replace-homedir>is-absolute>is-relative>is-unc-path": {
@@ -4280,11 +4186,6 @@
"console.warn": true
}
},
- "eslint-import-resolver-typescript>tsconfig-paths>json5": {
- "globals": {
- "console.warn": true
- }
- },
"eslint-plugin-import>tsconfig-paths>json5": {
"globals": {
"console.warn": true
@@ -4311,7 +4212,7 @@
"console.error": true
},
"packages": {
- "eslint-plugin-react>jsx-ast-utils>object.assign": true
+ "gulp>vinyl-fs>object.assign": true
}
},
"gulp>glob-watcher>just-debounce": {
@@ -4455,8 +4356,8 @@
"console.log": true
},
"packages": {
- "lavamoat>lavamoat-tofu>@babel/parser": true,
- "lavamoat>lavamoat-tofu>@babel/traverse": true
+ "@babel/core>@babel/parser": true,
+ "depcheck>@babel/traverse": true
}
},
"lavamoat>lavamoat-core>merge-deep>clone-deep>lazy-cache": {
@@ -4758,7 +4659,7 @@
"loose-envify": true,
"browserify>parents": true,
"browserify>module-deps>readable-stream": true,
- "browserify>resolve": true,
+ "depcheck>resolve": true,
"browserify>module-deps>stream-combiner2": true,
"browserify>module-deps>through2": true,
"watchify>xtend": true
@@ -4895,34 +4796,15 @@
"WeakRef": true
}
},
- "string.prototype.matchall>side-channel>object-inspect": {
- "builtin": {
- "util.inspect": true
- },
- "globals": {
- "HTMLElement": true,
- "WeakRef": true
- }
- },
"gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>object-visit": {
"packages": {
"gulp>gulp-cli>isobject": true
}
},
- "eslint-plugin-react>jsx-ast-utils>object.assign": {
- "packages": {
- "string.prototype.matchall>call-bind": true,
- "string.prototype.matchall>define-properties": true,
- "string.prototype.matchall>has-symbols": true,
- "@lavamoat/lavapack>json-stable-stringify>object-keys": true
- }
- },
"gulp>vinyl-fs>object.assign": {
"packages": {
"string.prototype.matchall>call-bind": true,
- "gulp>vinyl-fs>object.assign>call-bound": true,
"string.prototype.matchall>define-properties": true,
- "string.prototype.matchall>es-abstract>es-object-atoms": true,
"string.prototype.matchall>has-symbols": true,
"@lavamoat/lavapack>json-stable-stringify>object-keys": true
}
@@ -7191,9 +7073,9 @@
},
"string.prototype.matchall>regexp.prototype.flags": {
"packages": {
- "string.prototype.matchall>regexp.prototype.flags>call-bind": true,
+ "string.prototype.matchall>call-bind": true,
"string.prototype.matchall>define-properties": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"string.prototype.matchall>regexp.prototype.flags>set-function-name": true
}
},
@@ -7348,37 +7230,7 @@
"gulp>vinyl-fs>value-or-function": true
}
},
- "lavamoat>@lavamoat/aa>resolve": {
- "builtin": {
- "fs.readFileSync": true,
- "fs.realpathSync": true,
- "fs.statSync": true,
- "os.homedir": true,
- "path.dirname": true,
- "path.join": true,
- "path.parse": true,
- "path.relative": true,
- "path.resolve": true
- },
- "globals": {
- "process.env.HOME": true,
- "process.env.HOMEDRIVE": true,
- "process.env.HOMEPATH": true,
- "process.env.LNAME": true,
- "process.env.LOGNAME": true,
- "process.env.USER": true,
- "process.env.USERNAME": true,
- "process.env.USERPROFILE": true,
- "process.getuid": true,
- "process.platform": true,
- "process.versions.pnp": true
- },
- "packages": {
- "depcheck>is-core-module": true,
- "depcheck>resolve>path-parse": true
- }
- },
- "browserify>resolve": {
+ "depcheck>resolve": {
"builtin": {
"fs.readFile": true,
"fs.readFileSync": true,
@@ -7408,7 +7260,7 @@
"process.versions.pnp": true
},
"packages": {
- "browserify>resolve>is-core-module": true,
+ "depcheck>is-core-module": true,
"depcheck>resolve>path-parse": true
}
},
@@ -7666,8 +7518,8 @@
},
"string.prototype.matchall>es-abstract>safe-regex-test": {
"packages": {
- "string.prototype.matchall>es-abstract>safe-regex-test>call-bind": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"string.prototype.matchall>es-abstract>is-regex": true
}
},
@@ -7807,8 +7659,8 @@
"string.prototype.matchall>call-bind>set-function-length": {
"packages": {
"string.prototype.matchall>define-properties>define-data-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
+ "string.prototype.matchall>get-intrinsic": true,
"string.prototype.matchall>es-abstract>gopd": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true
}
@@ -7816,7 +7668,7 @@
"string.prototype.matchall>regexp.prototype.flags>set-function-name": {
"packages": {
"string.prototype.matchall>define-properties>define-data-property": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
+ "string.prototype.matchall>call-bind>es-errors": true,
"string.prototype.matchall>es-abstract>function.prototype.name>functions-have-names": true,
"string.prototype.matchall>es-abstract>has-property-descriptors": true
}
@@ -7848,36 +7700,11 @@
"@metamask/rpc-errors>fast-safe-stringify": true
}
},
- "string.prototype.matchall>side-channel>side-channel-list": {
- "packages": {
- "string.prototype.matchall>es-abstract>es-errors": true,
- "string.prototype.matchall>side-channel>object-inspect": true
- }
- },
- "string.prototype.matchall>side-channel>side-channel-map": {
- "packages": {
- "gulp>vinyl-fs>object.assign>call-bound": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
- "string.prototype.matchall>side-channel>object-inspect": true
- }
- },
- "string.prototype.matchall>side-channel>side-channel-weakmap": {
- "packages": {
- "gulp>vinyl-fs>object.assign>call-bound": true,
- "string.prototype.matchall>es-abstract>es-errors": true,
- "eslint-plugin-react>array-includes>get-intrinsic": true,
- "string.prototype.matchall>side-channel>object-inspect": true,
- "string.prototype.matchall>side-channel>side-channel-map": true
- }
- },
"string.prototype.matchall>side-channel": {
"packages": {
- "string.prototype.matchall>es-abstract>es-errors": true,
- "string.prototype.matchall>side-channel>object-inspect": true,
- "string.prototype.matchall>side-channel>side-channel-list": true,
- "string.prototype.matchall>side-channel>side-channel-map": true,
- "string.prototype.matchall>side-channel>side-channel-weakmap": true
+ "string.prototype.matchall>call-bind": true,
+ "string.prototype.matchall>get-intrinsic": true,
+ "string.prototype.matchall>es-abstract>object-inspect": true
}
},
"nyc>signal-exit": {
@@ -8823,7 +8650,7 @@
"ts-node>yn": true
}
},
- "eslint-import-resolver-typescript>tsconfig-paths": {
+ "eslint-plugin-import>tsconfig-paths": {
"builtin": {
"fs.existsSync": true,
"fs.lstatSync": true,
@@ -8844,24 +8671,9 @@
"process.cwd": true,
"process.env": true
},
- "packages": {
- "eslint-import-resolver-typescript>tsconfig-paths>json5": true,
- "wait-on>minimist": true,
- "eslint-plugin-import>tsconfig-paths>strip-bom": true
- }
- },
- "eslint-plugin-import>tsconfig-paths": {
- "builtin": {
- "fs.existsSync": true,
- "fs.lstatSync": true,
- "fs.readFileSync": true,
- "fs.statSync": true,
- "path.dirname": true,
- "path.join": true,
- "path.resolve": true
- },
"packages": {
"eslint-plugin-import>tsconfig-paths>json5": true,
+ "wait-on>minimist": true,
"eslint-plugin-import>tsconfig-paths>strip-bom": true
}
},
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index fa6eb97dd0b2..10878944bde9 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -113,6 +113,7 @@ import { hasTransactionData } from '../../shared/modules/transaction.utils';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { createDeepEqualSelector } from '../../shared/modules/selectors/util';
import { isSnapIgnoredInProd } from '../helpers/utils/snaps';
+// eslint-disable-next-line import/no-restricted-paths
import { getManifestFlags } from '../../app/scripts/lib/manifestFlags';
import {
getAllUnapprovedTransactions,
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index e1c131d08ca0..06ddacaee77c 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -6,6 +6,8 @@ import {
} from '@metamask/keyring-api';
import { deepClone } from '@metamask/snaps-utils';
import { TransactionStatus } from '@metamask/transaction-controller';
+// eslint-disable-next-line import/no-restricted-paths
+import * as manifestFlags from '../../app/scripts/lib/manifestFlags';
import { KeyringType } from '../../shared/constants/keyring';
import mockState from '../../test/data/mock-state.json';
import { CHAIN_IDS, NETWORK_TYPES } from '../../shared/constants/network';
@@ -15,7 +17,6 @@ import { DeleteRegulationStatus } from '../../shared/constants/metametrics';
import { selectSwitchedNetworkNeverShowMessage } from '../components/app/toast-master/selectors';
import * as networkSelectors from '../../shared/modules/selectors/networks';
import * as selectors from './selectors';
-import * as manifestFlags from '../../app/scripts/lib/manifestFlags';
jest.mock('../../shared/modules/selectors/networks', () => ({
...jest.requireActual('../../shared/modules/selectors/networks'),
@@ -2160,7 +2161,9 @@ describe('#getConnectedSitesList', () => {
beforeEach(() => {
// Mock the getManifestFlags function before each test
- getManifestFlagsMock = jest.spyOn(manifestFlags, 'getManifestFlags').mockReturnValue({});
+ getManifestFlagsMock = jest
+ .spyOn(manifestFlags, 'getManifestFlags')
+ .mockReturnValue({});
});
afterEach(() => {
From 4cef487d3dea7f3bd9076401713611fc810ae14b Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 15:54:19 +0000
Subject: [PATCH 03/20] feat(27255): move the file to git ignore and add
instructions to ReadMe for creating a remote feature flag
---
.gitignore | 3 +++
.manifest-flags.json.dist | 5 ++++
README.md | 11 +++++++++
development/build/manifest.js | 24 ++++++++++++++++++-
.../utils/plugins/ManifestPlugin/helpers.ts | 7 +++++-
manifest-flags.json | 8 -------
6 files changed, 48 insertions(+), 10 deletions(-)
create mode 100644 .manifest-flags.json.dist
delete mode 100644 manifest-flags.json
diff --git a/.gitignore b/.gitignore
index 6ee150dd8653..ab47e255bbe9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,9 @@ notes.txt
.metamaskrc
.metamaskprodrc
+# Manifest customizated configuration
+.manifest-flags.json
+
# Test results
test-results/
diff --git a/.manifest-flags.json.dist b/.manifest-flags.json.dist
new file mode 100644
index 000000000000..098779458f11
--- /dev/null
+++ b/.manifest-flags.json.dist
@@ -0,0 +1,5 @@
+ // This configuration file is used to manage values passing to _flags that will be injected into manifest.json
+{
+ "remoteFeatureFlags": {
+ }
+}
diff --git a/README.md b/README.md
index f76e913a6b6c..15c4daaa30aa 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,17 @@ If you are not a MetaMask Internal Developer, or are otherwise developing on a f
- If debugging MetaMetrics, you'll need to add a value for `SEGMENT_WRITE_KEY` [Segment write key](https://segment.com/docs/connections/find-writekey/), see [Developing on MetaMask - Segment](./development/README.md#segment).
- If debugging unhandled exceptions, you'll need to add a value for `SENTRY_DSN` [Sentry Dsn](https://docs.sentry.io/product/sentry-basics/dsn-explainer/), see [Developing on MetaMask - Sentry](./development/README.md#sentry).
- Optionally, replace the `PASSWORD` value with your development wallet password to avoid entering it each time you open the app.
+- Duplicate `manifest-flags.json.dist` within the root and rename it to `manifest-flags.json` by running `cp .manifest-flags.json{.dist,}`. This file is used to add flags to `manifest.json` build files for the extension. You can add flags to the file to be used in the build process, for example:
+ ```json
+ {
+ "remoteFeatureFlags": {
+ "testFlagForThreshold": {
+ "name": "test-flag",
+ "value": "test-value"
+ }
+ }
+ }
+ ```
- Run `yarn install` to install the dependencies.
- Build the project to the `./dist/` folder with `yarn dist` (for Chromium-based browsers) or `yarn dist:mv2` (for Firefox)
diff --git a/development/build/manifest.js b/development/build/manifest.js
index 17f6168df7dc..36e75352bf55 100644
--- a/development/build/manifest.js
+++ b/development/build/manifest.js
@@ -11,7 +11,6 @@ const baradDurManifest = isManifestV3
? require('../../app/manifest/v3/_barad_dur.json')
: require('../../app/manifest/v2/_barad_dur.json');
const { loadBuildTypesConfig } = require('../lib/build-type');
-const manifestFlags = require('../../manifest-flags.json');
const { TASKS, ENVIRONMENT } = require('./constants');
const { createTask, composeSeries } = require('./task');
@@ -19,6 +18,29 @@ const { getEnvironment, getBuildName } = require('./utils');
module.exports = createManifestTasks;
+async function loadManifestFlags() {
+ try {
+ return JSON.parse(
+ await fs.readFile(
+ path.join(__dirname, '../../.manifest-flags.json'),
+ 'utf8',
+ ),
+ );
+ } catch (error) {
+ return { remoteFeatureFlags: {} };
+ }
+}
+
+// Initialize with default value
+let manifestFlags = { remoteFeatureFlags: {} };
+
+// Load flags asynchronously
+loadManifestFlags().then((flags) => {
+ manifestFlags = flags;
+});
+
+module.exports = createManifestTasks;
+
function createManifestTasks({
browserPlatforms,
browserVersionMap,
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index 9f8104eb3c0a..01ef6fdb725b 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -1,4 +1,9 @@
-import manifestFlags from '../../../../../manifest-flags.json';
+let manifestFlags: { remoteFeatureFlags: Record };
+try {
+ manifestFlags = require('../../../../../.manifest-flags.json');
+} catch (error) {
+ manifestFlags = { remoteFeatureFlags: {} };
+}
/**
* Returns a function that will transform a manifest JSON object based on the
diff --git a/manifest-flags.json b/manifest-flags.json
deleted file mode 100644
index f90de282f373..000000000000
--- a/manifest-flags.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "remoteFeatureFlags": {
- "testFlagForThreshold": {
- "name": "test-flag",
- "value": "121212"
- }
- }
-}
From 5e3788b55a730488932bfe33107251729bed12b8 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 21:13:06 +0000
Subject: [PATCH 04/20] feat(29629): Add _flags to webpack build only when in
development; and restore default value for webpack config without passing in
`--test`
---
.../webpack/test/plugins.ManifestPlugin.test.ts | 2 +-
development/webpack/test/webpack.config.test.ts | 6 ++++--
.../webpack/utils/plugins/ManifestPlugin/helpers.ts | 10 +++++++---
development/webpack/webpack.config.ts | 2 +-
4 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/development/webpack/test/plugins.ManifestPlugin.test.ts b/development/webpack/test/plugins.ManifestPlugin.test.ts
index ff14904bb8d6..def2588dcb9c 100644
--- a/development/webpack/test/plugins.ManifestPlugin.test.ts
+++ b/development/webpack/test/plugins.ManifestPlugin.test.ts
@@ -232,7 +232,7 @@ describe('ManifestPlugin', () => {
function runTest(baseManifest: Combination) {
const manifest = baseManifest as unknown as chrome.runtime.Manifest;
const hasTabsPermission = (manifest.permissions || []).includes('tabs');
- const transform = transformManifest(args);
+ const transform = transformManifest(args, false);
if (args.test && hasTabsPermission) {
it("throws in test mode when manifest already contains 'tabs' permission", () => {
diff --git a/development/webpack/test/webpack.config.test.ts b/development/webpack/test/webpack.config.test.ts
index 796a2233bdd3..7a872af6be2c 100644
--- a/development/webpack/test/webpack.config.test.ts
+++ b/development/webpack/test/webpack.config.test.ts
@@ -78,7 +78,7 @@ ${Object.entries(env)
}
it('should have the correct defaults', () => {
- const config: Configuration = getWebpackConfig(['--test']);
+ const config: Configuration = getWebpackConfig([]);
// check that options are valid
const { options } = webpack(config);
assert.strictEqual(options.name, 'MetaMask – development');
@@ -162,8 +162,10 @@ ${Object.entries(env)
{
manifest_version: 3,
name: 'name',
- permissions: ['tabs'],
version: '1.2.3',
+ _flags: {
+ remoteFeatureFlags: {},
+ },
content_scripts: [
{
js: ['scripts/contentscript.js', 'scripts/inpage.js'],
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index 01ef6fdb725b..4c496f56a6c7 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -16,11 +16,15 @@ try {
* @param args
* @param args.lockdown
* @param args.test
+ * @param isDevelopment
* @returns a function that will transform the manifest JSON object
* @throws an error if the manifest already contains the "tabs" permission and
* `test` is `true`
*/
-export function transformManifest(args: { lockdown: boolean; test: boolean }) {
+export function transformManifest(
+ args: { lockdown: boolean; test: boolean },
+ isDevelopment: boolean,
+) {
const transforms: ((manifest: chrome.runtime.Manifest) => void)[] = [];
function removeLockdown(browserManifest: chrome.runtime.Manifest) {
@@ -40,8 +44,8 @@ export function transformManifest(args: { lockdown: boolean; test: boolean }) {
browserManifest._flags = manifestFlags;
}
- // Add manifest flags only for non-test builds so the test build is not affected by local feature flags
- if (!args.test) {
+ // Add manifest flags only for development builds
+ if (isDevelopment) {
transforms.push(addManifestFlags);
}
diff --git a/development/webpack/webpack.config.ts b/development/webpack/webpack.config.ts
index 9ec97b5507e1..17d9d8a7bc45 100644
--- a/development/webpack/webpack.config.ts
+++ b/development/webpack/webpack.config.ts
@@ -131,7 +131,7 @@ const plugins: WebpackPluginInstance[] = [
version: version.version,
versionName: version.versionName,
browsers: args.browser,
- transform: transformManifest(args),
+ transform: transformManifest(args, isDevelopment),
zip: args.zip,
...(args.zip
? {
From 5c703f2ed3c1c7ed98a57f80f4b97c1b4ecad08a Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 21:23:54 +0000
Subject: [PATCH 05/20] feat(29629): adapted to right pattern in transform
function
---
.../webpack/utils/plugins/ManifestPlugin/helpers.ts | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index 4c496f56a6c7..b58d11c1eb58 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -35,6 +35,11 @@ export function transformManifest(
}
}
+ if (!args.lockdown) {
+ // remove lockdown scripts from content_scripts
+ transforms.push(removeLockdown);
+ }
+
/**
* This function sets predefined flags in the manifest's _flags property.
*
@@ -44,16 +49,11 @@ export function transformManifest(
browserManifest._flags = manifestFlags;
}
- // Add manifest flags only for development builds
if (isDevelopment) {
+ // Add manifest flags only for development builds
transforms.push(addManifestFlags);
}
- if (!args.lockdown) {
- // remove lockdown scripts from content_scripts
- transforms.push(removeLockdown);
- }
-
function addTabsPermission(browserManifest: chrome.runtime.Manifest) {
if (browserManifest.permissions) {
if (browserManifest.permissions.includes('tabs')) {
From 35fe1f03a900b79e7400f84dc48a7afe05f19ffd Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 21:37:30 +0000
Subject: [PATCH 06/20] feat(29629): relocate `getManifestFlags` to
`shared/lib/manifestFlags`
---
app/scripts/lib/setupSentry.js | 2 +-
development/lib/get-manifest-flag.ts | 2 +-
{app/scripts => shared}/lib/manifestFlags.ts | 0
test/e2e/set-manifest-flags.ts | 2 +-
ui/helpers/utils/mm-lazy.ts | 3 +--
ui/selectors/selectors.js | 3 +--
ui/selectors/selectors.test.js | 3 +--
7 files changed, 6 insertions(+), 9 deletions(-)
rename {app/scripts => shared}/lib/manifestFlags.ts (100%)
diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js
index 76d66eba88d2..40327377c44e 100644
--- a/app/scripts/lib/setupSentry.js
+++ b/app/scripts/lib/setupSentry.js
@@ -3,8 +3,8 @@ import * as Sentry from '@sentry/browser';
import { logger } from '@sentry/utils';
import browser from 'webextension-polyfill';
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
+import { getManifestFlags } from '../../../shared/lib/manifestFlags';
import extractEthjsErrorMessage from './extractEthjsErrorMessage';
-import { getManifestFlags } from './manifestFlags';
import { filterEvents } from './sentry-filter-events';
const projectLogger = createProjectLogger('sentry');
diff --git a/development/lib/get-manifest-flag.ts b/development/lib/get-manifest-flag.ts
index 225b3a05121c..05a133ce0093 100644
--- a/development/lib/get-manifest-flag.ts
+++ b/development/lib/get-manifest-flag.ts
@@ -6,7 +6,7 @@ import { exec as callbackExec } from 'node:child_process';
import { hasProperty } from '@metamask/utils';
import { merge } from 'lodash';
-import type { ManifestFlags } from '../../app/scripts/lib/manifestFlags';
+import type { ManifestFlags } from '../../shared/lib/manifestFlags';
const exec = promisify(callbackExec);
const PR_BODY_FILEPATH = path.resolve(
diff --git a/app/scripts/lib/manifestFlags.ts b/shared/lib/manifestFlags.ts
similarity index 100%
rename from app/scripts/lib/manifestFlags.ts
rename to shared/lib/manifestFlags.ts
diff --git a/test/e2e/set-manifest-flags.ts b/test/e2e/set-manifest-flags.ts
index 8a7e45050f14..1a824808c90e 100644
--- a/test/e2e/set-manifest-flags.ts
+++ b/test/e2e/set-manifest-flags.ts
@@ -1,6 +1,6 @@
import fs from 'fs';
import { merge } from 'lodash';
-import { ManifestFlags } from '../../app/scripts/lib/manifestFlags';
+import { ManifestFlags } from '../../shared/lib/manifestFlags';
import { fetchManifestFlagsFromPRAndGit } from '../../development/lib/get-manifest-flag';
export const folder = `dist/${process.env.SELENIUM_BROWSER}`;
diff --git a/ui/helpers/utils/mm-lazy.ts b/ui/helpers/utils/mm-lazy.ts
index e31c22dfc99a..71a3d5d42e8a 100644
--- a/ui/helpers/utils/mm-lazy.ts
+++ b/ui/helpers/utils/mm-lazy.ts
@@ -1,6 +1,5 @@
import React from 'react';
-// eslint-disable-next-line import/no-restricted-paths
-import { getManifestFlags } from '../../../app/scripts/lib/manifestFlags';
+import { getManifestFlags } from '../../../shared/lib/manifestFlags';
import { endTrace, trace, TraceName } from '../../../shared/lib/trace';
type DynamicImportType = () => Promise<{ default: React.ComponentType }>;
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 10878944bde9..60d7892912b7 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -113,8 +113,7 @@ import { hasTransactionData } from '../../shared/modules/transaction.utils';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { createDeepEqualSelector } from '../../shared/modules/selectors/util';
import { isSnapIgnoredInProd } from '../helpers/utils/snaps';
-// eslint-disable-next-line import/no-restricted-paths
-import { getManifestFlags } from '../../app/scripts/lib/manifestFlags';
+import { getManifestFlags } from '../../shared/lib/manifestFlags';
import {
getAllUnapprovedTransactions,
getCurrentNetworkTransactions,
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index 06ddacaee77c..1cbd1a2a6e58 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -6,8 +6,7 @@ import {
} from '@metamask/keyring-api';
import { deepClone } from '@metamask/snaps-utils';
import { TransactionStatus } from '@metamask/transaction-controller';
-// eslint-disable-next-line import/no-restricted-paths
-import * as manifestFlags from '../../app/scripts/lib/manifestFlags';
+import * as manifestFlags from '../../shared/lib/manifestFlags';
import { KeyringType } from '../../shared/constants/keyring';
import mockState from '../../test/data/mock-state.json';
import { CHAIN_IDS, NETWORK_TYPES } from '../../shared/constants/network';
From 473a3bd2b5ff63be8da0f6a452d76edd371af1d0 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 21:47:21 +0000
Subject: [PATCH 07/20] feat(29629): enrich `getRemoteFeatureFlags` return type
---
ui/selectors/selectors.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 60d7892912b7..da0e2b78025c 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -2984,7 +2984,7 @@ export function getMetaMetricsDataDeletionStatus(state) {
* Otherwise returns the remote feature flags from the MetaMask state that's retrieved from controller.
*
* @param {object} state - The MetaMask state object
- * @returns {object} The remote feature flags object containing feature flag key-value pairs
+ * @returns {ManifestFlags['remoteFeatureFlags']} The remote feature flags object containing feature flag key-value pairs
*/
export function getRemoteFeatureFlags(state) {
const remoteFeatureFlagsValueFromManifest =
From caa4b4f9780a94e51c494f08800c7ffc3d2ff981 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Thu, 16 Jan 2025 22:54:23 +0000
Subject: [PATCH 08/20] feat(29629): shallow merge states in
`getRemoteFeatureFlags`
---
ui/selectors/selectors.js | 24 +++++++++++-------------
ui/selectors/selectors.test.js | 30 ++++++++++++++++++++++++++----
2 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index da0e2b78025c..c5b90310f8d7 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -2979,23 +2979,21 @@ export function getMetaMetricsDataDeletionStatus(state) {
}
/**
- * Gets the remote feature flags from either the manifest or state.
- * First checks if remote feature flags exist in the manifest and returns those if present.
- * Otherwise returns the remote feature flags from the MetaMask state that's retrieved from controller.
+ * Gets the remote feature flags by combining flags from both the manifest and state.
+ * Manifest flags take precedence and will override any duplicate flags from state.
+ * This allows for both static (manifest) and dynamic (state) feature flag configuration.
*
* @param {object} state - The MetaMask state object
- * @returns {ManifestFlags['remoteFeatureFlags']} The remote feature flags object containing feature flag key-value pairs
+ * @returns {object} Combined feature flags object with manifest flags taking precedence over state flags
*/
export function getRemoteFeatureFlags(state) {
- const remoteFeatureFlagsValueFromManifest =
- getManifestFlags().remoteFeatureFlags;
- if (
- remoteFeatureFlagsValueFromManifest &&
- Object.keys(remoteFeatureFlagsValueFromManifest).length > 0
- ) {
- return remoteFeatureFlagsValueFromManifest;
- }
- return state.metamask.remoteFeatureFlags;
+ const manifestFlags = getManifestFlags().remoteFeatureFlags;
+ const stateFlags = state.metamask.remoteFeatureFlags;
+
+ return {
+ ...stateFlags,
+ ...manifestFlags,
+ };
}
/**
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index 1cbd1a2a6e58..d6a940a4a676 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -2170,22 +2170,44 @@ describe('#getConnectedSitesList', () => {
getManifestFlagsMock.mockRestore();
});
- it('returns manifest flags when they are provided in manifest-flags.json', () => {
+ it('performs shallow merge of manifest flags and state flags', () => {
getManifestFlagsMock.mockReturnValue({
remoteFeatureFlags: {
- manifestFlag1: true,
- manifestFlag2: false,
+ flag1: true,
+ flag2: false,
},
});
const state = {
metamask: {
remoteFeatureFlags: {
- stateFlag: true,
+ flag1: false,
+ flag3: false,
},
},
};
+ expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
+ flag1: true,
+ flag2: false,
+ flag3: false,
+ });
+ });
+
+ it('returns manifest flags when they are only provided by manifest-flags.json', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {
+ manifestFlag1: true,
+ manifestFlag2: false,
+ },
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {},
+ },
+ };
+
expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
manifestFlag1: true,
manifestFlag2: false,
From 0bff314e795eac9730a1cc7d6be262e5ae48246d Mon Sep 17 00:00:00 2001
From: MetaMask Bot
Date: Thu, 16 Jan 2025 23:07:49 +0000
Subject: [PATCH 09/20] Update LavaMoat policies
---
lavamoat/build-system/policy.json | 8 --------
1 file changed, 8 deletions(-)
diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json
index 5c9dbd68b17c..545922814d1f 100644
--- a/lavamoat/build-system/policy.json
+++ b/lavamoat/build-system/policy.json
@@ -1795,7 +1795,6 @@
"chokidar>anymatch": true,
"chokidar>braces": true,
"chokidar>fsevents": true,
- "tsx>fsevents": true,
"eslint>glob-parent": true,
"chokidar>is-binary-path": true,
"del>is-glob": true,
@@ -3365,13 +3364,6 @@
"gulp-watch>chokidar>fsevents>node-pre-gyp": true
}
},
- "tsx>fsevents": {
- "globals": {
- "console.assert": true,
- "process.platform": true
- },
- "native": true
- },
"@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog>gauge": {
"builtin": {
"util.format": true
From d4e79a7d4b01987ffe4089d52afdc4f658c5868e Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Fri, 17 Jan 2025 11:10:37 +0000
Subject: [PATCH 10/20] feat(29629): fix e2e test due to shallow merging
---
test/e2e/constants.ts | 4 ++--
test/e2e/page-objects/pages/developer-options-page.ts | 10 ++++++++--
.../remote-feature-flag/remote-feature-flag.spec.ts | 2 +-
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts
index 42a4e0fc3f12..07565881c648 100644
--- a/test/e2e/constants.ts
+++ b/test/e2e/constants.ts
@@ -98,9 +98,9 @@ export const MOCK_REMOTE_FEATURE_FLAGS_RESPONSE = {
},
};
-/* Mock customized remote feature flags response */
+/* Mock customized remote feature flags response*/
export const MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS = {
- feature1: {
+ feature3: {
name: 'groupA',
value: 'valueA',
},
diff --git a/test/e2e/page-objects/pages/developer-options-page.ts b/test/e2e/page-objects/pages/developer-options-page.ts
index 8d37fc4091f3..61cd09e3383d 100644
--- a/test/e2e/page-objects/pages/developer-options-page.ts
+++ b/test/e2e/page-objects/pages/developer-options-page.ts
@@ -1,6 +1,9 @@
import { strict as assert } from 'assert';
import { Driver } from '../../webdriver/driver';
-import { MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS } from '../../constants';
+import {
+ MOCK_REMOTE_FEATURE_FLAGS_RESPONSE,
+ MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS,
+} from '../../constants';
class DevelopOptions {
private readonly driver: Driver;
@@ -47,7 +50,10 @@ class DevelopOptions {
const remoteFeatureFlagsState = await element.getText();
assert.equal(
remoteFeatureFlagsState,
- JSON.stringify(MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS),
+ JSON.stringify({
+ ...MOCK_REMOTE_FEATURE_FLAGS_RESPONSE,
+ ...MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS,
+ }),
);
}
}
diff --git a/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts b/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
index 48c8dd7f5b28..b65c827e5fe5 100644
--- a/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
+++ b/test/e2e/tests/remote-feature-flag/remote-feature-flag.spec.ts
@@ -52,7 +52,7 @@ describe('Remote feature flag', function (this: Suite) {
);
});
- it('offers the option to pass into manifest file for developers', async function () {
+ it('offers the option to pass into manifest file for developers along with original response', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder()
From 8e88e7fb2a00d1e365d73fbae798e0cfabc17246 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Fri, 17 Jan 2025 13:54:12 +0000
Subject: [PATCH 11/20] feat(29629): Update readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 15c4daaa30aa..ecdab37ca81e 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ If you are not a MetaMask Internal Developer, or are otherwise developing on a f
- If debugging MetaMetrics, you'll need to add a value for `SEGMENT_WRITE_KEY` [Segment write key](https://segment.com/docs/connections/find-writekey/), see [Developing on MetaMask - Segment](./development/README.md#segment).
- If debugging unhandled exceptions, you'll need to add a value for `SENTRY_DSN` [Sentry Dsn](https://docs.sentry.io/product/sentry-basics/dsn-explainer/), see [Developing on MetaMask - Sentry](./development/README.md#sentry).
- Optionally, replace the `PASSWORD` value with your development wallet password to avoid entering it each time you open the app.
-- Duplicate `manifest-flags.json.dist` within the root and rename it to `manifest-flags.json` by running `cp .manifest-flags.json{.dist,}`. This file is used to add flags to `manifest.json` build files for the extension. You can add flags to the file to be used in the build process, for example:
+- Duplicate `manifest-flags.json.dist` within the root and rename it to `manifest-flags.json` by running `cp .manifest-flags.json{.dist,}`. This file is used to add flags to `.manifest.json` build files for the extension. You can add flags to the file to be used in the build process, for example:
```json
{
"remoteFeatureFlags": {
From b1a52ae542c9efcb6a57a70f3cb33f1c1207fde1 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Fri, 17 Jan 2025 17:14:10 +0000
Subject: [PATCH 12/20] feat(29629): Remove unused [] param in getWebpackConfig
test
---
development/webpack/test/webpack.config.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/development/webpack/test/webpack.config.test.ts b/development/webpack/test/webpack.config.test.ts
index 7a872af6be2c..94439084b052 100644
--- a/development/webpack/test/webpack.config.test.ts
+++ b/development/webpack/test/webpack.config.test.ts
@@ -78,7 +78,7 @@ ${Object.entries(env)
}
it('should have the correct defaults', () => {
- const config: Configuration = getWebpackConfig([]);
+ const config: Configuration = getWebpackConfig();
// check that options are valid
const { options } = webpack(config);
assert.strictEqual(options.name, 'MetaMask – development');
From b2b58623abf962e3e283a60f82178490a5f4dae3 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Fri, 17 Jan 2025 17:16:40 +0000
Subject: [PATCH 13/20] feat(29629): Remove unnecessary --test flag in
non-default options test and revert changes on this test
---
development/webpack/test/webpack.config.test.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/development/webpack/test/webpack.config.test.ts b/development/webpack/test/webpack.config.test.ts
index 94439084b052..7bda64031448 100644
--- a/development/webpack/test/webpack.config.test.ts
+++ b/development/webpack/test/webpack.config.test.ts
@@ -194,7 +194,6 @@ ${Object.entries(env)
'--no-progress',
'--no-cache',
'--zip',
- '--test',
...removeUnsupportedFeatures,
],
{
@@ -235,7 +234,7 @@ ${Object.entries(env)
assert.deepStrictEqual(manifestPlugin.options.description, null);
assert.deepStrictEqual(manifestPlugin.options.zip, true);
assert(manifestPlugin.options.zipOptions, 'Zip options should be present');
- assert.notEqual(manifestPlugin.options.transform, undefined);
+ assert.deepStrictEqual(manifestPlugin.options.transform, undefined);
const progressPlugin = instance.options.plugins.find(
(plugin) => plugin && plugin.constructor.name === 'ProgressPlugin',
From a5ea54f8dad5e227c2e9d83b31fa4e66314b2b6b Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Fri, 17 Jan 2025 22:45:08 +0000
Subject: [PATCH 14/20] feat(29629): Remove comment in json file to avoid copy
error; read .manifest-flags.json file sync way in webpack build
---
.manifest-flags.json.dist | 1 -
.../test/plugins.ManifestPlugin.test.ts | 52 +++++++++++++++++++
.../utils/plugins/ManifestPlugin/helpers.ts | 26 +++++++---
3 files changed, 70 insertions(+), 9 deletions(-)
diff --git a/.manifest-flags.json.dist b/.manifest-flags.json.dist
index 098779458f11..d8a21d8df5fc 100644
--- a/.manifest-flags.json.dist
+++ b/.manifest-flags.json.dist
@@ -1,4 +1,3 @@
- // This configuration file is used to manage values passing to _flags that will be injected into manifest.json
{
"remoteFeatureFlags": {
}
diff --git a/development/webpack/test/plugins.ManifestPlugin.test.ts b/development/webpack/test/plugins.ManifestPlugin.test.ts
index def2588dcb9c..2bc0b116e403 100644
--- a/development/webpack/test/plugins.ManifestPlugin.test.ts
+++ b/development/webpack/test/plugins.ManifestPlugin.test.ts
@@ -281,4 +281,56 @@ describe('ManifestPlugin', () => {
}
}
});
+
+ describe('manifest flags in development mode', () => {
+ const testManifest = {} as chrome.runtime.Manifest;
+ const mockFlags = { remoteFeatureFlags: { testFlag: true } };
+
+ it('adds manifest flags in development mode', () => {
+ const transform = transformManifest({ lockdown: true, test: false }, true);
+ assert(transform, 'transform should be truthy');
+
+ // Mock fs.readFileSync
+ const fs = require('fs');
+ const originalReadFileSync = fs.readFileSync;
+ fs.readFileSync = () => JSON.stringify(mockFlags);
+
+ try {
+ const transformed = transform(testManifest, 'chrome');
+ assert.deepStrictEqual(
+ transformed._flags,
+ mockFlags,
+ 'manifest should have flags in development mode'
+ );
+ } finally {
+ // Restore original readFileSync
+ fs.readFileSync = originalReadFileSync;
+ }
+ });
+
+ it('handles missing manifest flags file', () => {
+ const transform = transformManifest({ lockdown: true, test: false }, true);
+ assert(transform, 'transform should be truthy');
+
+ // Mock fs.readFileSync to throw ENOENT
+ const fs = require('fs');
+ const originalReadFileSync = fs.readFileSync;
+ fs.readFileSync = () => {
+ const error = new Error('ENOENT');
+ throw error;
+ };
+
+ try {
+ const transformed = transform(testManifest, 'chrome');
+ assert.deepStrictEqual(
+ transformed._flags,
+ { remoteFeatureFlags: {} },
+ 'manifest should have default flags when file is missing'
+ );
+ } finally {
+ // Restore original readFileSync
+ fs.readFileSync = originalReadFileSync;
+ }
+ });
+ });
});
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index b58d11c1eb58..a59743c60b36 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -1,10 +1,3 @@
-let manifestFlags: { remoteFeatureFlags: Record };
-try {
- manifestFlags = require('../../../../../.manifest-flags.json');
-} catch (error) {
- manifestFlags = { remoteFeatureFlags: {} };
-}
-
/**
* Returns a function that will transform a manifest JSON object based on the
* given build args.
@@ -41,11 +34,28 @@ export function transformManifest(
}
/**
- * This function sets predefined flags in the manifest's _flags property.
+ * This function sets predefined flags in the manifest's _flags property
+ * that are stored in the .manifest-flags.json file.
*
* @param browserManifest - The Chrome extension manifest object to modify
*/
function addManifestFlags(browserManifest: chrome.runtime.Manifest) {
+ let manifestFlags = { remoteFeatureFlags: {} };
+
+ try {
+ const fs = require('fs');
+ const manifestFlagsContent = fs.readFileSync(
+ '.manifest-flags.json',
+ 'utf8',
+ );
+ manifestFlags = JSON.parse(manifestFlagsContent);
+ } catch (error: unknown) {
+ // Only ignore the error if the file doesn't exist
+ if (error instanceof Error && error.message !== 'ENOENT') {
+ throw error;
+ }
+ }
+
browserManifest._flags = manifestFlags;
}
From 5308bed8ec062195685cd4199d01f306ff422009 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 11:33:42 +0000
Subject: [PATCH 15/20] feat(29629): Move getRemoteFeatureFlags selector to a
separate ts file
---
.../developer-options-tab.test.tsx | 3 +-
.../developer-options-tab.tsx | 2 +-
ui/selectors/remote-feature-flag.ts | 19 ++++
ui/selectors/remote-feature-flags.test.ts | 96 +++++++++++++++++++
ui/selectors/selectors.js | 19 ----
ui/selectors/selectors.test.js | 95 ------------------
6 files changed, 117 insertions(+), 117 deletions(-)
create mode 100644 ui/selectors/remote-feature-flag.ts
create mode 100644 ui/selectors/remote-feature-flags.test.ts
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
index 5f97bc987323..e955e76ba7ef 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
@@ -20,8 +20,7 @@ jest.mock('../../../store/actions.ts', () => ({
mockSetServiceWorkerKeepAlivePreference,
}));
-jest.mock('../../../selectors', () => ({
- ...jest.requireActual('../../../selectors'),
+jest.mock('../../../selectors/remote-feature-flag', () => ({
getRemoteFeatureFlags: jest.fn(() => mockRemoteFeatureFlags),
}));
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
index 866e711d821c..f42199884af1 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
@@ -37,7 +37,7 @@ import {
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { getIsRedesignedConfirmationsDeveloperEnabled } from '../../confirmations/selectors/confirm';
-import { getRemoteFeatureFlags } from '../../../selectors';
+import { getRemoteFeatureFlags } from '../../../selectors/remote-feature-flag';
import ToggleRow from './developer-options-toggle-row-component';
import SentryTest from './sentry-test';
import { ProfileSyncDevSettings } from './profile-sync';
diff --git a/ui/selectors/remote-feature-flag.ts b/ui/selectors/remote-feature-flag.ts
new file mode 100644
index 000000000000..caa28a2410d6
--- /dev/null
+++ b/ui/selectors/remote-feature-flag.ts
@@ -0,0 +1,19 @@
+import { getManifestFlags } from '../../shared/lib/manifestFlags';
+
+/**
+ * Gets the remote feature flags by combining flags from both the manifest and state.
+ * Manifest flags take precedence and will override any duplicate flags from state.
+ * This allows for both static (manifest) and dynamic (state) feature flag configuration.
+ *
+ * @param state - The MetaMask state object
+ * @returns Combined feature flags object with manifest flags taking precedence over state flags
+ */
+export function getRemoteFeatureFlags(state) {
+ const manifestFlags = getManifestFlags().remoteFeatureFlags;
+ const stateFlags = state.metamask.remoteFeatureFlags;
+
+ return {
+ ...stateFlags,
+ ...manifestFlags,
+ };
+}
diff --git a/ui/selectors/remote-feature-flags.test.ts b/ui/selectors/remote-feature-flags.test.ts
new file mode 100644
index 000000000000..8005d1326c9d
--- /dev/null
+++ b/ui/selectors/remote-feature-flags.test.ts
@@ -0,0 +1,96 @@
+import * as manifestFlags from '../../shared/lib/manifestFlags';
+import { getRemoteFeatureFlags } from './remote-feature-flag';
+
+describe('#getRemoteFeatureFlags', () => {
+ let getManifestFlagsMock: jest.SpyInstance;
+
+ beforeEach(() => {
+ // Mock the getManifestFlags function before each test
+ getManifestFlagsMock = jest
+ .spyOn(manifestFlags, 'getManifestFlags')
+ .mockReturnValue({});
+ });
+
+ afterEach(() => {
+ // Clean up mock after each test
+ getManifestFlagsMock.mockRestore();
+ });
+
+ it('performs shallow merge of manifest flags and state flags', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {
+ flag1: true,
+ flag2: false,
+ },
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {
+ flag1: false,
+ flag3: false,
+ },
+ },
+ };
+
+ expect(getRemoteFeatureFlags(state)).toStrictEqual({
+ flag1: true,
+ flag2: false,
+ flag3: false,
+ });
+ });
+
+ it('returns manifest flags when they are only provided by manifest-flags.json', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {
+ manifestFlag1: true,
+ manifestFlag2: false,
+ },
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {},
+ },
+ };
+
+ expect(getRemoteFeatureFlags(state)).toStrictEqual({
+ manifestFlag1: true,
+ manifestFlag2: false,
+ });
+ });
+
+ it('returns state flags when manifest flags are empty', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: {},
+ });
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {
+ stateFlag: true,
+ },
+ },
+ };
+
+ expect(getRemoteFeatureFlags(state)).toStrictEqual({
+ stateFlag: true,
+ });
+ });
+
+ it('returns state flags when manifest flags are undefined', () => {
+ getManifestFlagsMock.mockReturnValue({});
+
+ const state = {
+ metamask: {
+ remoteFeatureFlags: {
+ stateFlag: true,
+ },
+ },
+ };
+
+ expect(getRemoteFeatureFlags(state)).toStrictEqual({
+ stateFlag: true,
+ });
+ });
+});
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index c5b90310f8d7..31c0ef436de0 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -113,7 +113,6 @@ import { hasTransactionData } from '../../shared/modules/transaction.utils';
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
import { createDeepEqualSelector } from '../../shared/modules/selectors/util';
import { isSnapIgnoredInProd } from '../helpers/utils/snaps';
-import { getManifestFlags } from '../../shared/lib/manifestFlags';
import {
getAllUnapprovedTransactions,
getCurrentNetworkTransactions,
@@ -2978,24 +2977,6 @@ export function getMetaMetricsDataDeletionStatus(state) {
return state.metamask.metaMetricsDataDeletionStatus;
}
-/**
- * Gets the remote feature flags by combining flags from both the manifest and state.
- * Manifest flags take precedence and will override any duplicate flags from state.
- * This allows for both static (manifest) and dynamic (state) feature flag configuration.
- *
- * @param {object} state - The MetaMask state object
- * @returns {object} Combined feature flags object with manifest flags taking precedence over state flags
- */
-export function getRemoteFeatureFlags(state) {
- const manifestFlags = getManifestFlags().remoteFeatureFlags;
- const stateFlags = state.metamask.remoteFeatureFlags;
-
- return {
- ...stateFlags,
- ...manifestFlags,
- };
-}
-
/**
* To get all installed snaps with proper metadata
*
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index d6a940a4a676..7036f2575c15 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -6,7 +6,6 @@ import {
} from '@metamask/keyring-api';
import { deepClone } from '@metamask/snaps-utils';
import { TransactionStatus } from '@metamask/transaction-controller';
-import * as manifestFlags from '../../shared/lib/manifestFlags';
import { KeyringType } from '../../shared/constants/keyring';
import mockState from '../../test/data/mock-state.json';
import { CHAIN_IDS, NETWORK_TYPES } from '../../shared/constants/network';
@@ -2155,100 +2154,6 @@ describe('#getConnectedSitesList', () => {
});
});
- describe('#getRemoteFeatureFlags', () => {
- let getManifestFlagsMock;
-
- beforeEach(() => {
- // Mock the getManifestFlags function before each test
- getManifestFlagsMock = jest
- .spyOn(manifestFlags, 'getManifestFlags')
- .mockReturnValue({});
- });
-
- afterEach(() => {
- // Clean up mock after each test
- getManifestFlagsMock.mockRestore();
- });
-
- it('performs shallow merge of manifest flags and state flags', () => {
- getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {
- flag1: true,
- flag2: false,
- },
- });
-
- const state = {
- metamask: {
- remoteFeatureFlags: {
- flag1: false,
- flag3: false,
- },
- },
- };
-
- expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
- flag1: true,
- flag2: false,
- flag3: false,
- });
- });
-
- it('returns manifest flags when they are only provided by manifest-flags.json', () => {
- getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {
- manifestFlag1: true,
- manifestFlag2: false,
- },
- });
-
- const state = {
- metamask: {
- remoteFeatureFlags: {},
- },
- };
-
- expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
- manifestFlag1: true,
- manifestFlag2: false,
- });
- });
-
- it('returns state flags when manifest flags are empty', () => {
- getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {},
- });
-
- const state = {
- metamask: {
- remoteFeatureFlags: {
- stateFlag: true,
- },
- },
- };
-
- expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
- stateFlag: true,
- });
- });
-
- it('returns state flags when manifest flags are undefined', () => {
- getManifestFlagsMock.mockReturnValue({});
-
- const state = {
- metamask: {
- remoteFeatureFlags: {
- stateFlag: true,
- },
- },
- };
-
- expect(selectors.getRemoteFeatureFlags(state)).toStrictEqual({
- stateFlag: true,
- });
- });
- });
-
describe('getIsTokenNetworkFilterEqualCurrentNetwork', () => {
beforeEach(() => {
process.env.PORTFOLIO_VIEW = 'true';
From 2cc15607d22f8ff797ff7827b7bc9e02564aafa4 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 11:37:20 +0000
Subject: [PATCH 16/20] feat(29629): Rename remote-feature-flags.ts file name
---
.../developer-options-tab/developer-options-tab.test.tsx | 3 ++-
.../settings/developer-options-tab/developer-options-tab.tsx | 2 +-
ui/selectors/index.js | 1 +
ui/selectors/remote-feature-flags.test.ts | 2 +-
.../{remote-feature-flag.ts => remote-feature-flags.ts} | 0
5 files changed, 5 insertions(+), 3 deletions(-)
rename ui/selectors/{remote-feature-flag.ts => remote-feature-flags.ts} (100%)
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
index e955e76ba7ef..5f97bc987323 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.test.tsx
@@ -20,7 +20,8 @@ jest.mock('../../../store/actions.ts', () => ({
mockSetServiceWorkerKeepAlivePreference,
}));
-jest.mock('../../../selectors/remote-feature-flag', () => ({
+jest.mock('../../../selectors', () => ({
+ ...jest.requireActual('../../../selectors'),
getRemoteFeatureFlags: jest.fn(() => mockRemoteFeatureFlags),
}));
diff --git a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
index f42199884af1..866e711d821c 100644
--- a/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
+++ b/ui/pages/settings/developer-options-tab/developer-options-tab.tsx
@@ -37,7 +37,7 @@ import {
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
import { getIsRedesignedConfirmationsDeveloperEnabled } from '../../confirmations/selectors/confirm';
-import { getRemoteFeatureFlags } from '../../../selectors/remote-feature-flag';
+import { getRemoteFeatureFlags } from '../../../selectors';
import ToggleRow from './developer-options-toggle-row-component';
import SentryTest from './sentry-test';
import { ProfileSyncDevSettings } from './profile-sync';
diff --git a/ui/selectors/index.js b/ui/selectors/index.js
index 290c70fb2a31..e1dcd187ec40 100644
--- a/ui/selectors/index.js
+++ b/ui/selectors/index.js
@@ -8,3 +8,4 @@ export * from './selectors';
export * from './transactions';
export * from './approvals';
export * from './accounts';
+export * from './remote-feature-flags';
diff --git a/ui/selectors/remote-feature-flags.test.ts b/ui/selectors/remote-feature-flags.test.ts
index 8005d1326c9d..9c62e5b34729 100644
--- a/ui/selectors/remote-feature-flags.test.ts
+++ b/ui/selectors/remote-feature-flags.test.ts
@@ -1,5 +1,5 @@
import * as manifestFlags from '../../shared/lib/manifestFlags';
-import { getRemoteFeatureFlags } from './remote-feature-flag';
+import { getRemoteFeatureFlags } from './remote-feature-flags';
describe('#getRemoteFeatureFlags', () => {
let getManifestFlagsMock: jest.SpyInstance;
diff --git a/ui/selectors/remote-feature-flag.ts b/ui/selectors/remote-feature-flags.ts
similarity index 100%
rename from ui/selectors/remote-feature-flag.ts
rename to ui/selectors/remote-feature-flags.ts
From 248ec01ff147253884c18464451992729007ac1f Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 19:54:41 +0000
Subject: [PATCH 17/20] feat(29629): Refactor getRemoteFeatureFlags to use safe
merge from lodash
---
.../test/plugins.ManifestPlugin.test.ts | 14 ++-
ui/selectors/remote-feature-flags.test.ts | 90 +++++++++++++------
ui/selectors/remote-feature-flags.ts | 15 ++--
3 files changed, 83 insertions(+), 36 deletions(-)
diff --git a/development/webpack/test/plugins.ManifestPlugin.test.ts b/development/webpack/test/plugins.ManifestPlugin.test.ts
index 2bc0b116e403..b26fdad6d791 100644
--- a/development/webpack/test/plugins.ManifestPlugin.test.ts
+++ b/development/webpack/test/plugins.ManifestPlugin.test.ts
@@ -287,7 +287,10 @@ describe('ManifestPlugin', () => {
const mockFlags = { remoteFeatureFlags: { testFlag: true } };
it('adds manifest flags in development mode', () => {
- const transform = transformManifest({ lockdown: true, test: false }, true);
+ const transform = transformManifest(
+ { lockdown: true, test: false },
+ true,
+ );
assert(transform, 'transform should be truthy');
// Mock fs.readFileSync
@@ -300,7 +303,7 @@ describe('ManifestPlugin', () => {
assert.deepStrictEqual(
transformed._flags,
mockFlags,
- 'manifest should have flags in development mode'
+ 'manifest should have flags in development mode',
);
} finally {
// Restore original readFileSync
@@ -309,7 +312,10 @@ describe('ManifestPlugin', () => {
});
it('handles missing manifest flags file', () => {
- const transform = transformManifest({ lockdown: true, test: false }, true);
+ const transform = transformManifest(
+ { lockdown: true, test: false },
+ true,
+ );
assert(transform, 'transform should be truthy');
// Mock fs.readFileSync to throw ENOENT
@@ -325,7 +331,7 @@ describe('ManifestPlugin', () => {
assert.deepStrictEqual(
transformed._flags,
{ remoteFeatureFlags: {} },
- 'manifest should have default flags when file is missing'
+ 'manifest should have default flags when file is missing',
);
} finally {
// Restore original readFileSync
diff --git a/ui/selectors/remote-feature-flags.test.ts b/ui/selectors/remote-feature-flags.test.ts
index 9c62e5b34729..f02eed536626 100644
--- a/ui/selectors/remote-feature-flags.test.ts
+++ b/ui/selectors/remote-feature-flags.test.ts
@@ -1,5 +1,36 @@
import * as manifestFlags from '../../shared/lib/manifestFlags';
-import { getRemoteFeatureFlags } from './remote-feature-flags';
+import {
+ getRemoteFeatureFlags,
+ RemoteFeatureFlagsState,
+} from './remote-feature-flags';
+
+const MOCK_DATA = {
+ manifestFlags: {
+ basic: {
+ flag1: true,
+ flag2: false,
+ },
+ empty: {},
+ nested: {
+ flag1: { b: 3 },
+ flag2: false,
+ },
+ },
+ stateFlags: {
+ basic: {
+ flag1: false,
+ flag3: false,
+ },
+ empty: {},
+ withStateFlag: {
+ stateFlag: true,
+ },
+ nested: {
+ flag1: { a: 1, b: 2 },
+ flag3: false,
+ },
+ },
+};
describe('#getRemoteFeatureFlags', () => {
let getManifestFlagsMock: jest.SpyInstance;
@@ -18,18 +49,12 @@ describe('#getRemoteFeatureFlags', () => {
it('performs shallow merge of manifest flags and state flags', () => {
getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {
- flag1: true,
- flag2: false,
- },
+ remoteFeatureFlags: MOCK_DATA.manifestFlags.basic,
});
- const state = {
+ const state: RemoteFeatureFlagsState = {
metamask: {
- remoteFeatureFlags: {
- flag1: false,
- flag3: false,
- },
+ remoteFeatureFlags: MOCK_DATA.stateFlags.basic,
},
};
@@ -42,34 +67,29 @@ describe('#getRemoteFeatureFlags', () => {
it('returns manifest flags when they are only provided by manifest-flags.json', () => {
getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {
- manifestFlag1: true,
- manifestFlag2: false,
- },
+ remoteFeatureFlags: MOCK_DATA.manifestFlags.basic,
});
- const state = {
+ const state: RemoteFeatureFlagsState = {
metamask: {
- remoteFeatureFlags: {},
+ remoteFeatureFlags: MOCK_DATA.stateFlags.empty,
},
};
expect(getRemoteFeatureFlags(state)).toStrictEqual({
- manifestFlag1: true,
- manifestFlag2: false,
+ flag1: true,
+ flag2: false,
});
});
it('returns state flags when manifest flags are empty', () => {
getManifestFlagsMock.mockReturnValue({
- remoteFeatureFlags: {},
+ remoteFeatureFlags: MOCK_DATA.manifestFlags.empty,
});
- const state = {
+ const state: RemoteFeatureFlagsState = {
metamask: {
- remoteFeatureFlags: {
- stateFlag: true,
- },
+ remoteFeatureFlags: MOCK_DATA.stateFlags.withStateFlag,
},
};
@@ -81,11 +101,9 @@ describe('#getRemoteFeatureFlags', () => {
it('returns state flags when manifest flags are undefined', () => {
getManifestFlagsMock.mockReturnValue({});
- const state = {
+ const state: RemoteFeatureFlagsState = {
metamask: {
- remoteFeatureFlags: {
- stateFlag: true,
- },
+ remoteFeatureFlags: MOCK_DATA.stateFlags.withStateFlag,
},
};
@@ -93,4 +111,22 @@ describe('#getRemoteFeatureFlags', () => {
stateFlag: true,
});
});
+
+ it('performs deep merge of manifest flags and state flags for nested objects', () => {
+ getManifestFlagsMock.mockReturnValue({
+ remoteFeatureFlags: MOCK_DATA.manifestFlags.nested,
+ });
+
+ const state: RemoteFeatureFlagsState = {
+ metamask: {
+ remoteFeatureFlags: MOCK_DATA.stateFlags.nested,
+ },
+ };
+
+ expect(getRemoteFeatureFlags(state)).toStrictEqual({
+ flag1: { a: 1, b: 3 },
+ flag2: false,
+ flag3: false,
+ });
+ });
});
diff --git a/ui/selectors/remote-feature-flags.ts b/ui/selectors/remote-feature-flags.ts
index caa28a2410d6..cb96e39a1d4b 100644
--- a/ui/selectors/remote-feature-flags.ts
+++ b/ui/selectors/remote-feature-flags.ts
@@ -1,5 +1,13 @@
+import merge from 'lodash/merge';
+import { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';
import { getManifestFlags } from '../../shared/lib/manifestFlags';
+export type RemoteFeatureFlagsState = {
+ metamask: {
+ remoteFeatureFlags: RemoteFeatureFlagControllerState['remoteFeatureFlags'];
+ };
+};
+
/**
* Gets the remote feature flags by combining flags from both the manifest and state.
* Manifest flags take precedence and will override any duplicate flags from state.
@@ -8,12 +16,9 @@ import { getManifestFlags } from '../../shared/lib/manifestFlags';
* @param state - The MetaMask state object
* @returns Combined feature flags object with manifest flags taking precedence over state flags
*/
-export function getRemoteFeatureFlags(state) {
+export function getRemoteFeatureFlags(state: RemoteFeatureFlagsState) {
const manifestFlags = getManifestFlags().remoteFeatureFlags;
const stateFlags = state.metamask.remoteFeatureFlags;
- return {
- ...stateFlags,
- ...manifestFlags,
- };
+ return merge({}, stateFlags, manifestFlags);
}
From 40f3c0795aa48d7f5a6e09de0c8ed328a3f93a15 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 19:55:52 +0000
Subject: [PATCH 18/20] feat(29629): Rename gitnore comment
---
.gitignore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index ab47e255bbe9..5f01faba0a09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,7 +52,7 @@ notes.txt
.metamaskrc
.metamaskprodrc
-# Manifest customizated configuration
+# Customized manifest configuration
.manifest-flags.json
# Test results
From 3a1601950757bdc9be599eb78d7c9899a0592cf3 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 20:06:22 +0000
Subject: [PATCH 19/20] feat(29629): Remove asyn reading for loadManifestFlags
in normal build
---
development/build/manifest.js | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/development/build/manifest.js b/development/build/manifest.js
index 36e75352bf55..479275df9127 100644
--- a/development/build/manifest.js
+++ b/development/build/manifest.js
@@ -31,13 +31,7 @@ async function loadManifestFlags() {
}
}
-// Initialize with default value
-let manifestFlags = { remoteFeatureFlags: {} };
-
-// Load flags asynchronously
-loadManifestFlags().then((flags) => {
- manifestFlags = flags;
-});
+const manifestFlags = loadManifestFlags();
module.exports = createManifestTasks;
From 77ad1e5adcc1083e80bf220e9cc18e3ed7652265 Mon Sep 17 00:00:00 2001
From: dddddanica
Date: Sun, 19 Jan 2025 21:19:19 +0000
Subject: [PATCH 20/20] feat(29629): Fix unit test of file not exist
---
development/webpack/utils/plugins/ManifestPlugin/helpers.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
index a59743c60b36..80cacfcb1233 100644
--- a/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
+++ b/development/webpack/utils/plugins/ManifestPlugin/helpers.ts
@@ -51,7 +51,7 @@ export function transformManifest(
manifestFlags = JSON.parse(manifestFlagsContent);
} catch (error: unknown) {
// Only ignore the error if the file doesn't exist
- if (error instanceof Error && error.message !== 'ENOENT') {
+ if (error instanceof Error && 'code' in error && error.code !== 'ENOENT') {
throw error;
}
}