@hot-updater/test-utils 1.0.0-rc.39 → 1.0.0-rc.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2797,6 +2797,21 @@ const createMeasuredDatabase = async (adapter, plugins, options = {}) => {
2797
2797
  };
2798
2798
  //#endregion
2799
2799
  //#region src/setupReadBudgetTestSuite.ts
2800
+ /** A published template: a greeting that beta devices get another value of. */
2801
+ const remoteConfigTemplate = (greeting) => ({
2802
+ conditions: [{
2803
+ name: "Beta",
2804
+ rules: [{
2805
+ type: "channel",
2806
+ channels: ["beta"]
2807
+ }]
2808
+ }],
2809
+ parameters: { greeting: {
2810
+ valueType: "STRING",
2811
+ defaultValue: { value: greeting },
2812
+ conditionalValues: { Beta: { value: `${greeting}, tester` } }
2813
+ } }
2814
+ });
2800
2815
  const DAY = 864e5;
2801
2816
  const HOUR = 36e5;
2802
2817
  /** The UTC midnight Insights events start at. */
@@ -2967,11 +2982,20 @@ const seed = async (database) => {
2967
2982
  await api.insights.recordEvent(failureOf(1, 3));
2968
2983
  await api.insights.recordEvent(failureOf(2, 5));
2969
2984
  await api.insights.recordEvent(failureOf(3, 7, true));
2985
+ for (const [baseVersion, greeting] of [
2986
+ "Hi",
2987
+ "Hello",
2988
+ "Hey"
2989
+ ].entries()) await api.remoteConfig.publish({
2990
+ template: remoteConfigTemplate(greeting),
2991
+ baseVersion
2992
+ });
2970
2993
  const channel = (name) => core.findChannelByName(name).then((found) => found);
2971
2994
  const productionId = (await channel("production")).id;
2972
2995
  return {
2973
2996
  core,
2974
2997
  insights: api.insights,
2998
+ remoteConfig: api.remoteConfig,
2975
2999
  clientAuth: database.clientAuth,
2976
3000
  apiKey,
2977
3001
  productionId,
@@ -3024,6 +3048,33 @@ const READ_BUDGETS = [
3024
3048
  },
3025
3049
  check: (catalog) => expect(catalog?.releases).toHaveLength(4)
3026
3050
  }),
3051
+ budget({
3052
+ api: "remote config fetch: 1 point read of the active template",
3053
+ read: ({ remoteConfig }) => remoteConfig.resolve({
3054
+ platform: "ios",
3055
+ channel: "beta"
3056
+ }),
3057
+ adapter: reads(1, 1, 0, 0),
3058
+ engine: {
3059
+ calls: 1,
3060
+ rows: 1
3061
+ },
3062
+ check: (resolved) => expect(resolved).toEqual({
3063
+ version: 3,
3064
+ values: { greeting: "Hey, tester" }
3065
+ })
3066
+ }),
3067
+ budget({
3068
+ api: "remote config versions page: limit rows from one query",
3069
+ read: ({ remoteConfig }) => remoteConfig.listVersions({ limit: 2 }),
3070
+ adapter: reads(0, 0, 1, 2),
3071
+ engine: {
3072
+ calls: 1,
3073
+ rows: 2
3074
+ },
3075
+ returned: (page) => page.versions.length,
3076
+ check: (page) => expect(page.versions.map(({ version }) => version)).toEqual([3, 2])
3077
+ }),
3027
3078
  budget({
3028
3079
  api: "artifact resolution: 1 batch get of 2 bundles and 1 unique patch read",
3029
3080
  read: ({ core }) => core.getArtifactInfo(createBundleFixture("106").id, createBundleFixture("105").id, 1),
@@ -3428,8 +3479,16 @@ const setupReadBudgetTestSuite = (options) => {
3428
3479
  let seeded;
3429
3480
  let writes = 0;
3430
3481
  beforeAll(async () => {
3431
- const [{ insights }, { apiKeys }] = await Promise.all([import("@hot-updater/server/plugins/insights"), import("@hot-updater/server/plugins/api-keys")]);
3432
- const plugins = [insights(), apiKeys()];
3482
+ const [{ insights }, { apiKeys }, { remoteConfig }] = await Promise.all([
3483
+ import("@hot-updater/server/plugins/insights"),
3484
+ import("@hot-updater/server/plugins/api-keys"),
3485
+ import("@hot-updater/server/plugins/remote-config")
3486
+ ]);
3487
+ const plugins = [
3488
+ insights(),
3489
+ apiKeys(),
3490
+ remoteConfig()
3491
+ ];
3433
3492
  created = await options.createAdapter({
3434
3493
  tables: [...toolingTargetOf(plugins).schema.tables, SETTINGS_TABLE],
3435
3494
  nativePageSize: NATIVE_PAGE_SIZE
@@ -12,6 +12,7 @@ let _hot_updater_protocol = require("@hot-updater/protocol");
12
12
  * runtime.hooks.onAppReady(launch);
13
13
  * await runtime.settled();
14
14
  * expect(runtime.requests[0]?.json()).toEqual(expectedEvent);
15
+ * runtime.api.someMethod(); // the API the plugin adds as hotUpdater.<id>
15
16
  * ```
16
17
  *
17
18
  * It sets plugins up with the plugin host from `@hot-updater/protocol`,
@@ -139,7 +140,7 @@ const setupClientPlugins = (plugins, options = {}) => {
139
140
  },
140
141
  now: () => options.now ? options.now() : Date.now()
141
142
  });
142
- host.configurePlugins(plugins, {
143
+ const apis = host.configurePlugins(plugins, {
143
144
  baseURL,
144
145
  requestHeaders: options.requestHeaders,
145
146
  requestTimeout: options.requestTimeout,
@@ -151,6 +152,7 @@ const setupClientPlugins = (plugins, options = {}) => {
151
152
  for (const settlement of host.dispatchPluginHook(name, payload)) track(settlement);
152
153
  };
153
154
  return {
155
+ apis,
154
156
  hooks: {
155
157
  onAppReady: call("onAppReady"),
156
158
  onUpdateCheck: call("onUpdateCheck"),
@@ -171,8 +173,15 @@ const setupClientPlugins = (plugins, options = {}) => {
171
173
  }
172
174
  };
173
175
  };
174
- /** Sets one plugin up; see `setupClientPlugins`. */
175
- const setupClientPlugin = (plugin, options) => setupClientPlugins([plugin], options);
176
+ /** Sets one plugin up; see `setupClientPlugins`. `api` is the plugin's API. */
177
+ const setupClientPlugin = (plugin, options) => {
178
+ const runtime = setupClientPlugins([plugin], options);
179
+ const apis = runtime.apis;
180
+ return {
181
+ ...runtime,
182
+ api: apis[plugin.id]
183
+ };
184
+ };
176
185
  //#endregion
177
186
  exports.createTestStorage = createTestStorage;
178
187
  exports.setupClientPlugin = setupClientPlugin;
@@ -1,4 +1,4 @@
1
- import { AppReadyResult, BundleDownloadedInfo, HotUpdaterBaseURL, HotUpdaterClientPlugin, UpdateCheckResult, UpdateError, UpdateHttpResponse } from "@hot-updater/protocol";
1
+ import { AppReadyResult, BundleDownloadedInfo, ClientPluginApi, ClientPluginApis, HotUpdaterBaseURL, HotUpdaterClientPlugin, UpdateCheckResult, UpdateError, UpdateHttpResponse } from "@hot-updater/protocol";
2
2
  //#region src/setupClientPlugins.d.ts
3
3
  /** A value the test fixes, or reads each time a plugin asks for it. */
4
4
  type TestValue<T> = T | (() => T);
@@ -77,7 +77,12 @@ interface ClientPluginTestHooks {
77
77
  onHttpResponse(response: UpdateHttpResponse): void;
78
78
  }
79
79
  /** One JavaScript runtime with the plugins set up, as after `HotUpdater.init`. */
80
- interface ClientPluginTestRuntime {
80
+ interface ClientPluginTestRuntime<TApis = Readonly<Record<string, unknown>>> {
81
+ /**
82
+ * The plugins' APIs by plugin id, as the instance `HotUpdater.init`
83
+ * returns has them.
84
+ */
85
+ readonly apis: TApis;
81
86
  /**
82
87
  * Calls a hook on every plugin that returned it, as the SDK does: without
83
88
  * waiting for it, and with a throw or rejection recorded in `errors`
@@ -104,8 +109,10 @@ interface ClientPluginTestRuntime {
104
109
  /** A new device's empty plugin storage. */
105
110
  declare const createTestStorage: () => ClientPluginTestStorage;
106
111
  /** Sets plugins up as `HotUpdater.init({ plugins })` does, in a new runtime. */
107
- declare const setupClientPlugins: (plugins: readonly HotUpdaterClientPlugin[], options?: ClientPluginTestOptions) => ClientPluginTestRuntime;
108
- /** Sets one plugin up; see `setupClientPlugins`. */
109
- declare const setupClientPlugin: (plugin: HotUpdaterClientPlugin, options?: ClientPluginTestOptions) => ClientPluginTestRuntime;
112
+ declare const setupClientPlugins: <const TPlugins extends readonly HotUpdaterClientPlugin[]>(plugins: TPlugins, options?: ClientPluginTestOptions) => ClientPluginTestRuntime<ClientPluginApis<TPlugins>>;
113
+ /** Sets one plugin up; see `setupClientPlugins`. `api` is the plugin's API. */
114
+ declare const setupClientPlugin: <const P extends HotUpdaterClientPlugin>(plugin: P, options?: ClientPluginTestOptions) => ClientPluginTestRuntime<ClientPluginApis<readonly [P]>> & {
115
+ readonly api: ClientPluginApi<P>;
116
+ };
110
117
  //#endregion
111
118
  export { ClientPluginTestHooks, ClientPluginTestOptions, ClientPluginTestRequest, ClientPluginTestRuntime, ClientPluginTestStorage, createTestStorage, setupClientPlugin, setupClientPlugins };
@@ -1,4 +1,4 @@
1
- import { AppReadyResult, BundleDownloadedInfo, HotUpdaterBaseURL, HotUpdaterClientPlugin, UpdateCheckResult, UpdateError, UpdateHttpResponse } from "@hot-updater/protocol";
1
+ import { AppReadyResult, BundleDownloadedInfo, ClientPluginApi, ClientPluginApis, HotUpdaterBaseURL, HotUpdaterClientPlugin, UpdateCheckResult, UpdateError, UpdateHttpResponse } from "@hot-updater/protocol";
2
2
  //#region src/setupClientPlugins.d.ts
3
3
  /** A value the test fixes, or reads each time a plugin asks for it. */
4
4
  type TestValue<T> = T | (() => T);
@@ -77,7 +77,12 @@ interface ClientPluginTestHooks {
77
77
  onHttpResponse(response: UpdateHttpResponse): void;
78
78
  }
79
79
  /** One JavaScript runtime with the plugins set up, as after `HotUpdater.init`. */
80
- interface ClientPluginTestRuntime {
80
+ interface ClientPluginTestRuntime<TApis = Readonly<Record<string, unknown>>> {
81
+ /**
82
+ * The plugins' APIs by plugin id, as the instance `HotUpdater.init`
83
+ * returns has them.
84
+ */
85
+ readonly apis: TApis;
81
86
  /**
82
87
  * Calls a hook on every plugin that returned it, as the SDK does: without
83
88
  * waiting for it, and with a throw or rejection recorded in `errors`
@@ -104,8 +109,10 @@ interface ClientPluginTestRuntime {
104
109
  /** A new device's empty plugin storage. */
105
110
  declare const createTestStorage: () => ClientPluginTestStorage;
106
111
  /** Sets plugins up as `HotUpdater.init({ plugins })` does, in a new runtime. */
107
- declare const setupClientPlugins: (plugins: readonly HotUpdaterClientPlugin[], options?: ClientPluginTestOptions) => ClientPluginTestRuntime;
108
- /** Sets one plugin up; see `setupClientPlugins`. */
109
- declare const setupClientPlugin: (plugin: HotUpdaterClientPlugin, options?: ClientPluginTestOptions) => ClientPluginTestRuntime;
112
+ declare const setupClientPlugins: <const TPlugins extends readonly HotUpdaterClientPlugin[]>(plugins: TPlugins, options?: ClientPluginTestOptions) => ClientPluginTestRuntime<ClientPluginApis<TPlugins>>;
113
+ /** Sets one plugin up; see `setupClientPlugins`. `api` is the plugin's API. */
114
+ declare const setupClientPlugin: <const P extends HotUpdaterClientPlugin>(plugin: P, options?: ClientPluginTestOptions) => ClientPluginTestRuntime<ClientPluginApis<readonly [P]>> & {
115
+ readonly api: ClientPluginApi<P>;
116
+ };
110
117
  //#endregion
111
118
  export { ClientPluginTestHooks, ClientPluginTestOptions, ClientPluginTestRequest, ClientPluginTestRuntime, ClientPluginTestStorage, createTestStorage, setupClientPlugin, setupClientPlugins };
@@ -11,6 +11,7 @@ import { createPluginHost, pluginStorageKey, resolveBaseURL } from "@hot-updater
11
11
  * runtime.hooks.onAppReady(launch);
12
12
  * await runtime.settled();
13
13
  * expect(runtime.requests[0]?.json()).toEqual(expectedEvent);
14
+ * runtime.api.someMethod(); // the API the plugin adds as hotUpdater.<id>
14
15
  * ```
15
16
  *
16
17
  * It sets plugins up with the plugin host from `@hot-updater/protocol`,
@@ -138,7 +139,7 @@ const setupClientPlugins = (plugins, options = {}) => {
138
139
  },
139
140
  now: () => options.now ? options.now() : Date.now()
140
141
  });
141
- host.configurePlugins(plugins, {
142
+ const apis = host.configurePlugins(plugins, {
142
143
  baseURL,
143
144
  requestHeaders: options.requestHeaders,
144
145
  requestTimeout: options.requestTimeout,
@@ -150,6 +151,7 @@ const setupClientPlugins = (plugins, options = {}) => {
150
151
  for (const settlement of host.dispatchPluginHook(name, payload)) track(settlement);
151
152
  };
152
153
  return {
154
+ apis,
153
155
  hooks: {
154
156
  onAppReady: call("onAppReady"),
155
157
  onUpdateCheck: call("onUpdateCheck"),
@@ -170,7 +172,14 @@ const setupClientPlugins = (plugins, options = {}) => {
170
172
  }
171
173
  };
172
174
  };
173
- /** Sets one plugin up; see `setupClientPlugins`. */
174
- const setupClientPlugin = (plugin, options) => setupClientPlugins([plugin], options);
175
+ /** Sets one plugin up; see `setupClientPlugins`. `api` is the plugin's API. */
176
+ const setupClientPlugin = (plugin, options) => {
177
+ const runtime = setupClientPlugins([plugin], options);
178
+ const apis = runtime.apis;
179
+ return {
180
+ ...runtime,
181
+ api: apis[plugin.id]
182
+ };
183
+ };
175
184
  //#endregion
176
185
  export { createTestStorage, setupClientPlugin, setupClientPlugins };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/test-utils",
3
- "version": "1.0.0-rc.39",
3
+ "version": "1.0.0-rc.41",
4
4
  "type": "module",
5
5
  "description": "Test utilities for Hot Updater packages",
6
6
  "sideEffects": false,
@@ -50,10 +50,10 @@
50
50
  "mysql2": "*",
51
51
  "pg": "*",
52
52
  "vitest": "^4.0.0",
53
- "@hot-updater/plugin-insights": "^1.0.0-rc.39",
54
- "@hot-updater/protocol": "^1.0.0-rc.39",
55
- "@hot-updater/plugin-core": "^1.0.0-rc.39",
56
- "@hot-updater/server": "^1.0.0-rc.39"
53
+ "@hot-updater/plugin-insights": "^1.0.0-rc.41",
54
+ "@hot-updater/protocol": "^1.0.0-rc.41",
55
+ "@hot-updater/plugin-core": "^1.0.0-rc.41",
56
+ "@hot-updater/server": "^1.0.0-rc.41"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "@electric-sql/pglite": {
@@ -83,11 +83,11 @@
83
83
  "typescript": "npm:@typescript/typescript6@6.0.2",
84
84
  "vitest": "4.1.4",
85
85
  "workspace-tools": "^0.41.7",
86
- "@hot-updater/plugin-insights": "1.0.0-rc.39",
87
- "@hot-updater/js": "1.0.0-rc.39",
88
- "@hot-updater/plugin-core": "1.0.0-rc.39",
89
- "@hot-updater/server": "1.0.0-rc.39",
90
- "@hot-updater/protocol": "1.0.0-rc.39"
86
+ "@hot-updater/plugin-insights": "1.0.0-rc.41",
87
+ "@hot-updater/protocol": "1.0.0-rc.41",
88
+ "@hot-updater/js": "1.0.0-rc.41",
89
+ "@hot-updater/plugin-core": "1.0.0-rc.41",
90
+ "@hot-updater/server": "1.0.0-rc.41"
91
91
  },
92
92
  "inlinedDependencies": {
93
93
  "@nodelib/fs.scandir": "2.1.5",