Skip to content

Commit 3314fcd

Browse files
committed
Merge branch 'main' into hm/update-notification-example
2 parents b5c8a6c + 5159929 commit 3314fcd

File tree

24 files changed

+353
-67
lines changed

24 files changed

+353
-67
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"test:clean": "yarn workspaces foreach --all --parallel --verbose run test:clean",
3838
"test:verbose": "yarn workspaces foreach --all --parallel --verbose run test:verbose",
3939
"test:watch": "yarn workspaces foreach --all --parallel --verbose run test:watch",
40+
"update-chrome": "./scripts/update-chrome.sh",
4041
"update-readme-content": "tsx ./scripts/update-readme-content.mts"
4142
},
4243
"simple-git-hooks": {
@@ -88,7 +89,7 @@
8889
"@typescript-eslint/eslint-plugin": "^5.42.1",
8990
"@typescript-eslint/parser": "^6.21.0",
9091
"@yarnpkg/types": "^4.0.0",
91-
"chromedriver": "^129.0.2",
92+
"chromedriver": "^131.0.5",
9293
"depcheck": "^1.4.7",
9394
"eslint": "^8.27.0",
9495
"eslint-config-prettier": "^8.5.0",

packages/examples/packages/browserify-plugin/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snaps.git"
88
},
99
"source": {
10-
"shasum": "hy0TMeQeqznNQRX2j7DnxRt1Nn5Z+v0rjaWNpe1fEWE=",
10+
"shasum": "nWxYWnUSrrm7uZeqQoIaP3l1hbk2ZWRKGULlxodyuhw=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/examples/packages/browserify/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snaps.git"
88
},
99
"source": {
10-
"shasum": "VR3Zwjo0yqKLkuKHGDfS9AmuyW3KMbXSmi9Nh9JgCMw=",
10+
"shasum": "WnX2s+XAfT18c6WH1hAniRAIgDEe7VyAieuRXFEDIEY=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/snaps-rpc-methods/jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
1010
],
1111
coverageThreshold: {
1212
global: {
13-
branches: 93.91,
14-
functions: 98.02,
15-
lines: 98.65,
16-
statements: 98.24,
13+
branches: 93.97,
14+
functions: 98.05,
15+
lines: 98.67,
16+
statements: 98.25,
1717
},
1818
},
1919
});

packages/snaps-rpc-methods/src/permitted/scheduleBackgroundEvent.test.ts

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,105 @@ describe('snap_scheduleBackgroundEvent', () => {
120120
});
121121
});
122122

123+
it('schedules a background event using duration', async () => {
124+
const { implementation } = scheduleBackgroundEventHandler;
125+
126+
const scheduleBackgroundEvent = jest.fn();
127+
const hasPermission = jest.fn().mockImplementation(() => true);
128+
129+
const hooks = {
130+
scheduleBackgroundEvent,
131+
hasPermission,
132+
};
133+
134+
const engine = new JsonRpcEngine();
135+
136+
engine.push(createOriginMiddleware(MOCK_SNAP_ID));
137+
engine.push((request, response, next, end) => {
138+
const result = implementation(
139+
request as JsonRpcRequest<ScheduleBackgroundEventParams>,
140+
response as PendingJsonRpcResponse<ScheduleBackgroundEventResult>,
141+
next,
142+
end,
143+
hooks,
144+
);
145+
146+
result?.catch(end);
147+
});
148+
149+
await engine.handle({
150+
jsonrpc: '2.0',
151+
id: 1,
152+
method: 'snap_scheduleBackgroundEvent',
153+
params: {
154+
duration: 'PT30S',
155+
request: {
156+
method: 'handleExport',
157+
params: ['p1'],
158+
},
159+
},
160+
});
161+
162+
expect(scheduleBackgroundEvent).toHaveBeenCalledWith({
163+
date: expect.any(String),
164+
request: {
165+
method: 'handleExport',
166+
params: ['p1'],
167+
},
168+
});
169+
});
170+
171+
it('throws on an invalid duration', async () => {
172+
const { implementation } = scheduleBackgroundEventHandler;
173+
174+
const scheduleBackgroundEvent = jest.fn();
175+
const hasPermission = jest.fn().mockImplementation(() => true);
176+
177+
const hooks = {
178+
scheduleBackgroundEvent,
179+
hasPermission,
180+
};
181+
182+
const engine = new JsonRpcEngine();
183+
184+
engine.push(createOriginMiddleware(MOCK_SNAP_ID));
185+
engine.push((request, response, next, end) => {
186+
const result = implementation(
187+
request as JsonRpcRequest<ScheduleBackgroundEventParams>,
188+
response as PendingJsonRpcResponse<ScheduleBackgroundEventResult>,
189+
next,
190+
end,
191+
hooks,
192+
);
193+
194+
result?.catch(end);
195+
});
196+
197+
const response = await engine.handle({
198+
jsonrpc: '2.0',
199+
id: 1,
200+
method: 'snap_scheduleBackgroundEvent',
201+
params: {
202+
duration: 'PQ30S',
203+
request: {
204+
method: 'handleExport',
205+
params: ['p1'],
206+
},
207+
},
208+
});
209+
210+
expect(response).toStrictEqual({
211+
error: {
212+
code: -32602,
213+
message:
214+
'Invalid params: At path: duration -- Not a valid ISO 8601 duration.',
215+
stack: expect.any(String),
216+
},
217+
id: 1,
218+
jsonrpc: '2.0',
219+
});
220+
});
221+
123222
it('throws if a snap does not have the "endowment:cronjob" permission', async () => {
124223
const { implementation } = scheduleBackgroundEventHandler;
125224

@@ -171,7 +270,7 @@ describe('snap_scheduleBackgroundEvent', () => {
171270
});
172271
});
173272

174-
it('throws if no timezone information is provided in the ISO8601 string', async () => {
273+
it('throws if no timezone information is provided in the ISO 8601 date', async () => {
175274
const { implementation } = scheduleBackgroundEventHandler;
176275

177276
const scheduleBackgroundEvent = jest.fn();
@@ -214,7 +313,7 @@ describe('snap_scheduleBackgroundEvent', () => {
214313
error: {
215314
code: -32602,
216315
message:
217-
'Invalid params: At path: date -- ISO 8601 string must have timezone information.',
316+
'Invalid params: At path: date -- ISO 8601 date must have timezone information.',
218317
stack: expect.any(String),
219318
},
220319
id: 1,
@@ -265,7 +364,7 @@ describe('snap_scheduleBackgroundEvent', () => {
265364
error: {
266365
code: -32602,
267366
message:
268-
'Invalid params: At path: date -- Not a valid ISO 8601 string.',
367+
'Invalid params: At path: date -- Not a valid ISO 8601 date.',
269368
stack: expect.any(String),
270369
},
271370
id: 1,

packages/snaps-rpc-methods/src/permitted/scheduleBackgroundEvent.ts

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
22
import type { PermittedHandlerExport } from '@metamask/permission-controller';
33
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
4-
import type {
5-
JsonRpcRequest,
6-
ScheduleBackgroundEventParams,
7-
ScheduleBackgroundEventResult,
4+
import {
5+
selectiveUnion,
6+
type JsonRpcRequest,
7+
type ScheduleBackgroundEventParams,
8+
type ScheduleBackgroundEventResult,
89
} from '@metamask/snaps-sdk';
910
import type { CronjobRpcRequest } from '@metamask/snaps-utils';
1011
import {
@@ -18,8 +19,12 @@ import {
1819
refine,
1920
string,
2021
} from '@metamask/superstruct';
21-
import { assert, type PendingJsonRpcResponse } from '@metamask/utils';
22-
import { DateTime } from 'luxon';
22+
import {
23+
assert,
24+
hasProperty,
25+
type PendingJsonRpcResponse,
26+
} from '@metamask/utils';
27+
import { DateTime, Duration } from 'luxon';
2328

2429
import { SnapEndowments } from '../endowments';
2530
import type { MethodHooksObject } from '../utils';
@@ -55,26 +60,61 @@ export const scheduleBackgroundEventHandler: PermittedHandlerExport<
5560
};
5661

5762
const offsetRegex = /Z|([+-]\d{2}:?\d{2})$/u;
58-
const ScheduleBackgroundEventsParametersStruct = object({
63+
64+
const ScheduleBackgroundEventParametersWithDateStruct = object({
5965
date: refine(string(), 'date', (val) => {
6066
const date = DateTime.fromISO(val);
6167
if (date.isValid) {
6268
// Luxon doesn't have a reliable way to check if timezone info was not provided
6369
if (!offsetRegex.test(val)) {
64-
return 'ISO 8601 string must have timezone information';
70+
return 'ISO 8601 date must have timezone information';
6571
}
6672
return true;
6773
}
68-
return 'Not a valid ISO 8601 string';
74+
return 'Not a valid ISO 8601 date';
75+
}),
76+
request: CronjobRpcRequestStruct,
77+
});
78+
79+
const ScheduleBackgroundEventParametersWithDurationStruct = object({
80+
duration: refine(string(), 'duration', (val) => {
81+
const duration = Duration.fromISO(val);
82+
if (!duration.isValid) {
83+
return 'Not a valid ISO 8601 duration';
84+
}
85+
return true;
6986
}),
7087
request: CronjobRpcRequestStruct,
7188
});
7289

90+
const ScheduleBackgroundEventParametersStruct = selectiveUnion((val) => {
91+
if (hasProperty(val, 'date')) {
92+
return ScheduleBackgroundEventParametersWithDateStruct;
93+
}
94+
return ScheduleBackgroundEventParametersWithDurationStruct;
95+
});
96+
7397
export type ScheduleBackgroundEventParameters = InferMatching<
74-
typeof ScheduleBackgroundEventsParametersStruct,
98+
typeof ScheduleBackgroundEventParametersStruct,
7599
ScheduleBackgroundEventParams
76100
>;
77101

102+
/**
103+
* Generates a `DateTime` object based on if a duration or date is provided.
104+
*
105+
* @param params - The validated params from the `snap_scheduleBackgroundEvent` call.
106+
* @returns A `DateTime` object.
107+
*/
108+
function getStartDate(params: ScheduleBackgroundEventParams) {
109+
if ('duration' in params) {
110+
return DateTime.fromJSDate(new Date())
111+
.toUTC()
112+
.plus(Duration.fromISO(params.duration));
113+
}
114+
115+
return DateTime.fromISO(params.date, { setZone: true });
116+
}
117+
78118
/**
79119
* The `snap_scheduleBackgroundEvent` method implementation.
80120
*
@@ -107,14 +147,14 @@ async function getScheduleBackgroundEventImplementation(
107147
try {
108148
const validatedParams = getValidatedParams(params);
109149

110-
const { date, request } = validatedParams;
150+
const { request } = validatedParams;
151+
152+
const date = getStartDate(validatedParams);
111153

112154
// Make sure any millisecond precision is removed.
113-
const truncatedDate = DateTime.fromISO(date, { setZone: true })
114-
.startOf('second')
115-
.toISO({
116-
suppressMilliseconds: true,
117-
});
155+
const truncatedDate = date.startOf('second').toISO({
156+
suppressMilliseconds: true,
157+
});
118158

119159
assert(truncatedDate);
120160

@@ -138,7 +178,7 @@ function getValidatedParams(
138178
params: unknown,
139179
): ScheduleBackgroundEventParameters {
140180
try {
141-
return create(params, ScheduleBackgroundEventsParametersStruct);
181+
return create(params, ScheduleBackgroundEventParametersStruct);
142182
} catch (error) {
143183
if (error instanceof StructError) {
144184
throw rpcErrors.invalidParams({

packages/snaps-rpc-methods/src/restricted/getPreferences.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ describe('snap_getPreferences', () => {
2626
describe('getImplementation', () => {
2727
it('returns the preferences', async () => {
2828
const methodHooks = {
29-
getPreferences: jest
30-
.fn()
31-
.mockReturnValue({ locale: 'en', currency: 'usd' }),
29+
getPreferences: jest.fn().mockReturnValue({
30+
locale: 'en',
31+
currency: 'usd',
32+
hideBalances: false,
33+
}),
3234
};
3335

3436
const implementation = getImplementation(methodHooks);
@@ -40,7 +42,7 @@ describe('snap_getPreferences', () => {
4042
},
4143
method: 'snap_getPreferences',
4244
}),
43-
).toStrictEqual({ locale: 'en', currency: 'usd' });
45+
).toStrictEqual({ locale: 'en', currency: 'usd', hideBalances: false });
4446
});
4547
});
4648
});

packages/snaps-sdk/src/jsx/components/Value.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Text } from './Text';
12
import { Value } from './Value';
23

34
describe('Value', () => {
@@ -13,4 +14,36 @@ describe('Value', () => {
1314
},
1415
});
1516
});
17+
18+
it('renders with text elements', () => {
19+
const result = (
20+
<Value
21+
value={<Text color="error">0.05 ETH</Text>}
22+
extra={<Text color="error">$200</Text>}
23+
/>
24+
);
25+
26+
expect(result).toStrictEqual({
27+
type: 'Value',
28+
key: null,
29+
props: {
30+
extra: {
31+
type: 'Text',
32+
key: null,
33+
props: {
34+
children: '$200',
35+
color: 'error',
36+
},
37+
},
38+
value: {
39+
type: 'Text',
40+
key: null,
41+
props: {
42+
children: '0.05 ETH',
43+
color: 'error',
44+
},
45+
},
46+
},
47+
});
48+
});
1649
});

0 commit comments

Comments
 (0)