Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Add snap_getPreferences #2607

Merged
merged 6 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/snaps-jest/src/internals/simulation/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export const DEFAULT_SRP =
*/
export const DEFAULT_LOCALE = 'en';

/**
* The default currency.
*/
export const DEFAULT_CURRENCY = 'usd';

/**
* The default JSON-RPC endpoint for Ethereum requests.
*/
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { getMockOptions } from '../../../../test-utils/options';
import { getGetPreferencesMethodImplementation } from './get-preferences';

describe('getGetPreferencesMethodImplementation', () => {
it('returns the implementation of the `getPreferences` hook', async () => {
const fn = getGetPreferencesMethodImplementation(
getMockOptions({
locale: 'en',
}),
);

expect(fn()).toStrictEqual({ currency: 'usd', locale: 'en' });
});

it('returns the implementation of the `getPreferences` hook for a different locale', async () => {
const fn = getGetPreferencesMethodImplementation(
getMockOptions({
locale: 'nl',
}),
);

expect(fn()).toStrictEqual({ currency: 'usd', locale: 'nl' });
});

it('returns the implementation of the `getPreferences` hook for a different currency', async () => {
const fn = getGetPreferencesMethodImplementation(
getMockOptions({
currency: 'dkk',
}),
);

expect(fn()).toStrictEqual({ currency: 'dkk', locale: 'en' });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { SimulationOptions } from '../../options';

/**
* Get the implementation of the `getPreferences` hook.
*
* @param options - The simulation options.
* @param options.currency - The currency to use.
* @param options.locale - The locale to use.
* @returns The implementation of the `getPreferences` hook.
*/
export function getGetPreferencesMethodImplementation({
currency,
locale,
}: SimulationOptions) {
return () => {
return { currency, locale };
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './get-locale';
export * from './get-preferences';
export * from './notifications';
export * from './request-user-approval';
export * from './state';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ describe('getPermissionSpecifications', () => {
],
"targetName": "snap_getLocale",
},
"snap_getPreferences": {
"allowedCaveats": null,
"methodImplementation": [Function],
"permissionType": "RestrictedMethod",
"subjectTypes": [
"snap",
],
"targetName": "snap_getPreferences",
},
"snap_manageAccounts": {
"allowedCaveats": null,
"methodImplementation": [Function],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
EXCLUDED_SNAP_PERMISSIONS,
} from './constants';
import {
getGetLocaleMethodImplementation,
getGetPreferencesMethodImplementation,
getClearSnapStateMethodImplementation,
getGetSnapStateMethodImplementation,
getUpdateSnapStateMethodImplementation,
Expand Down Expand Up @@ -88,7 +88,7 @@ export function getPermissionSpecifications({

// Snaps-specific hooks.
clearSnapState: getClearSnapStateMethodImplementation(runSaga),
getLocale: getGetLocaleMethodImplementation(options),
getPreferences: getGetPreferencesMethodImplementation(options),
getSnapState: getGetSnapStateMethodImplementation(runSaga),
getUnlockPromise: asyncResolve(true),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ describe('getOptions', () => {

expect(options).toMatchInlineSnapshot(`
{
"currency": "usd",
"locale": "en",
"secretRecoveryPhrase": "test test test test test test test test test test test ball",
"state": null,
Expand All @@ -16,11 +17,13 @@ describe('getOptions', () => {

it('returns the provided options', () => {
const options = getOptions({
currency: 'eur',
locale: 'nl',
});

expect(options).toMatchInlineSnapshot(`
{
"currency": "eur",
"locale": "nl",
"secretRecoveryPhrase": "test test test test test test test test test test test ball",
"state": null,
Expand Down
3 changes: 2 additions & 1 deletion packages/snaps-jest/src/internals/simulation/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {
} from '@metamask/superstruct';
import { JsonStruct } from '@metamask/utils';

import { DEFAULT_LOCALE, DEFAULT_SRP } from './constants';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE, DEFAULT_SRP } from './constants';

const SimulationOptionsStruct = object({
currency: defaulted(optional(string()), DEFAULT_CURRENCY),
secretRecoveryPhrase: defaulted(optional(string()), DEFAULT_SRP),
locale: defaulted(optional(string()), DEFAULT_LOCALE),
state: defaulted(optional(nullable(record(string(), JsonStruct))), null),
Expand Down
3 changes: 3 additions & 0 deletions packages/snaps-jest/src/test-utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import type { SimulationOptions } from '../internals';
* Get the options for the simulation.
*
* @param options - The options for the simulation.
* @param options.currency - The currency to use.
* @param options.locale - The locale to use.
* @param options.secretRecoveryPhrase - The secret recovery phrase to use.
* @param options.state - The state to use.
* @param options.unencryptedState - The unencrypted state to use.
* @returns The options for the simulation.
*/
export function getMockOptions({
currency = 'usd',
locale = 'en',
secretRecoveryPhrase = DEFAULT_SRP,
state = null,
unencryptedState = null,
}: Partial<SimulationOptions> = {}): SimulationOptions {
return {
currency,
locale,
secretRecoveryPhrase,
state,
Expand Down
9 changes: 9 additions & 0 deletions packages/snaps-rpc-methods/src/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ describe('buildSnapRestrictedMethodSpecifications', () => {
],
"targetName": "snap_getLocale",
},
"snap_getPreferences": {
"allowedCaveats": null,
"methodImplementation": [Function],
"permissionType": "RestrictedMethod",
"subjectTypes": [
"snap",
],
"targetName": "snap_getPreferences",
},
"snap_manageAccounts": {
"allowedCaveats": null,
"methodImplementation": [Function],
Expand Down
4 changes: 2 additions & 2 deletions packages/snaps-rpc-methods/src/restricted/getLocale.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('snap_getLocale', () => {
describe('specification', () => {
it('builds specification', () => {
const methodHooks = {
getLocale: jest.fn(),
getPreferences: jest.fn(),
};

expect(
Expand All @@ -26,7 +26,7 @@ describe('snap_getLocale', () => {
describe('getImplementation', () => {
it('returns the locale', async () => {
const methodHooks = {
getLocale: jest.fn().mockResolvedValue('en'),
getPreferences: jest.fn().mockReturnValue({ locale: 'en' }),
};

const implementation = getImplementation(methodHooks);
Expand Down
16 changes: 10 additions & 6 deletions packages/snaps-rpc-methods/src/restricted/getLocale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import type {
RestrictedMethodParameters,
} from '@metamask/permission-controller';
import { PermissionType, SubjectType } from '@metamask/permission-controller';
import type { GetLocaleResult } from '@metamask/snaps-sdk';
import type {
GetLocaleResult,
GetPreferencesResult,
} from '@metamask/snaps-sdk';
import type { NonEmptyArray } from '@metamask/utils';

import type { MethodHooksObject } from '../utils';

const methodName = 'snap_getLocale';

export type GetLocaleMethodHooks = {
getLocale: () => Promise<string>;
getPreferences: () => GetPreferencesResult;
};

type SpecificationBuilderOptions = {
Expand All @@ -36,6 +39,7 @@ type Specification = ValidPermissionSpecification<{
* @param options.allowedCaveats - The optional allowed caveats for the permission.
* @param options.methodHooks - The RPC method hooks needed by the method implementation.
* @returns The specification for the `snap_getLocale` permission.
* @deprecated - To be removed in favor of `snap_getPreferences`.
*/
export const specificationBuilder: PermissionSpecificationBuilder<
PermissionType.RestrictedMethod,
Expand All @@ -52,7 +56,7 @@ export const specificationBuilder: PermissionSpecificationBuilder<
};

const methodHooks: MethodHooksObject<GetLocaleMethodHooks> = {
getLocale: true,
getPreferences: true,
};

export const getLocaleBuilder = Object.freeze({
Expand All @@ -65,13 +69,13 @@ export const getLocaleBuilder = Object.freeze({
* Builds the method implementation for `snap_getLocale`.
*
* @param hooks - The RPC method hooks.
* @param hooks.getLocale - A function that returns the user selected locale.
* @param hooks.getPreferences - A function that returns the user selected preferences.
* @returns The user selected locale.
*/
export function getImplementation({ getLocale }: GetLocaleMethodHooks) {
export function getImplementation({ getPreferences }: GetLocaleMethodHooks) {
return async function implementation(
_args: RestrictedMethodOptions<RestrictedMethodParameters>,
): Promise<GetLocaleResult> {
return getLocale();
return getPreferences().locale;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { PermissionType, SubjectType } from '@metamask/permission-controller';

import { getImplementation, specificationBuilder } from './getPreferences';

describe('snap_getPreferences', () => {
describe('specification', () => {
it('builds specification', () => {
const methodHooks = {
getPreferences: jest.fn(),
};

expect(
specificationBuilder({
methodHooks,
}),
).toStrictEqual({
allowedCaveats: null,
methodImplementation: expect.anything(),
permissionType: PermissionType.RestrictedMethod,
targetName: 'snap_getPreferences',
subjectTypes: [SubjectType.Snap],
});
});
});

describe('getImplementation', () => {
it('returns the preferences', async () => {
const methodHooks = {
getPreferences: jest
.fn()
.mockReturnValue({ locale: 'en', currency: 'usd' }),
};

const implementation = getImplementation(methodHooks);

expect(
await implementation({
context: {
origin: 'npm:@metamask/example-snap',
},
method: 'snap_getPreferences',
}),
).toStrictEqual({ locale: 'en', currency: 'usd' });
});
});
});
Loading
Loading