From 1e04d3ded9c8f7c35d1c7553f5f6906a0cbda9a8 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Sat, 15 Aug 2026 10:12:31 -0500 Subject: [PATCH] Enforce strict ESLint block formatting --- eslint.config.mjs | 61 ++- package-lock.json | 53 ++ package.json | 1 + packages/angularjs/src/index.ts | 14 +- .../browser/src/plugins/BrowserErrorPlugin.ts | 4 +- .../src/plugins/BrowserGlobalHandlerPlugin.ts | 65 ++- .../test/plugins/BrowserErrorPlugin.test.ts | 6 +- ...BrowserIgnoreExtensionErrorsPlugin.test.ts | 8 +- packages/core/src/EventBuilder.ts | 16 +- packages/core/src/ExceptionlessClient.ts | 13 +- packages/core/src/Utils.ts | 26 +- .../core/src/configuration/Configuration.ts | 16 +- .../core/src/plugins/EventPluginContext.ts | 4 +- .../plugins/default/DuplicateCheckerPlugin.ts | 6 +- packages/core/src/queue/DefaultEventQueue.ts | 14 +- packages/core/src/storage/InMemoryStorage.ts | 8 +- .../core/test/DocumentationExamples.test.ts | 10 +- packages/core/test/Utils.test.ts | 513 ++++++++++++++++-- .../test/configuration/Configuration.test.ts | 7 +- .../ConfigurationDefaultsPlugin.test.ts | 13 +- .../default/EventExclusionPlugin.test.ts | 32 +- .../core/test/queue/DefaultEventQueue.test.ts | 39 +- .../submission/TestSubmissionClient.test.ts | 20 +- .../node/src/storage/NodeDirectoryStorage.ts | 4 +- .../test/storage/NodeDirectoryStorage.test.ts | 9 +- .../src/ExceptionlessErrorBoundary.tsx | 8 +- .../react-native/src/logging/CallbackLog.ts | 6 +- .../ReactNativeExceptionlessClient.test.ts | 10 +- .../test/logging/CallbackLog.test.ts | 14 +- .../test/plugins/NativeCrashPlugin.test.ts | 64 ++- .../ReactNativeEnvironmentInfoPlugin.test.ts | 30 +- .../plugins/ReactNativeErrorPlugin.test.ts | 8 +- .../ReactNativeGlobalHandlerPlugin.test.ts | 94 +++- .../ReactNativeLifeCyclePlugin.test.ts | 61 ++- .../test/storage/AsyncStorageProvider.test.ts | 8 +- .../react/src/ExceptionlessErrorBoundary.tsx | 8 +- scripts/eslint-rules.test.mjs | 73 +++ vitest.config.ts | 8 + 38 files changed, 1171 insertions(+), 183 deletions(-) create mode 100644 scripts/eslint-rules.test.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs index 9513ed0d..85facf92 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,11 +1,14 @@ import eslint from "@eslint/js"; +import stylistic from "@stylistic/eslint-plugin"; import { defineConfig } from "eslint/config"; import eslintConfigPrettier from "eslint-config-prettier"; import vitest from "@vitest/eslint-plugin"; import tseslint from "typescript-eslint"; export default defineConfig( - { ignores: ["**/dist/", "**/node_modules/", ".agents/", "example/", "**/expo-plugin/", "**/react-native.config.*"] }, + { + ignores: ["**/dist/", "**/node_modules/", ".agents/", "example/", "**/expo-plugin/", "**/react-native.config.*"] + }, eslint.configs.recommended, { extends: tseslint.configs.recommendedTypeChecked, @@ -27,11 +30,63 @@ export default defineConfig( "@typescript-eslint/no-duplicate-type-constituents": "off", "@typescript-eslint/restrict-plus-operands": "off", "@typescript-eslint/no-unused-vars": "error", - "@typescript-eslint/no-unused-expressions": ["error", { allowShortCircuit: true }], - "@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }] + "@typescript-eslint/no-unused-expressions": [ + "error", + { + allowShortCircuit: true + } + ], + "@typescript-eslint/no-misused-promises": [ + "error", + { + checksVoidReturn: false + } + ] + } + }, + { + files: ["scripts/**/*.mjs"], + ...tseslint.configs.disableTypeChecked, + languageOptions: { + ...tseslint.configs.disableTypeChecked.languageOptions, + globals: { + console: "readonly", + process: "readonly" + } } }, eslintConfigPrettier, + { + plugins: { + "@stylistic": stylistic + }, + rules: { + curly: ["error", "all"], + "@stylistic/object-curly-newline": [ + "error", + { + ObjectExpression: { + minProperties: 1 + } + } + ], + "@stylistic/brace-style": [ + "error", + "1tbs", + { + allowSingleLine: false + } + ], + "padding-line-between-statements": [ + "error", + { + blankLine: "always", + prev: "block-like", + next: ["if", "while", "for", "do"] + } + ] + } + }, { files: ["**/test/**/*.ts"], plugins: vitest.configs.recommended.plugins, diff --git a/package-lock.json b/package-lock.json index 863a7058..20626bf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ ], "devDependencies": { "@eslint/js": "^10.0.1", + "@stylistic/eslint-plugin": "^5.10.0", "@vitest/eslint-plugin": "^1.6.19", "eslint": "^10.4.1", "eslint-config-prettier": "^10.1.8", @@ -3656,6 +3657,58 @@ "dev": true, "license": "MIT" }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@sveltejs/acorn-typescript": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz", diff --git a/package.json b/package.json index b4c5d07f..245bcb8e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ ], "devDependencies": { "@eslint/js": "^10.0.1", + "@stylistic/eslint-plugin": "^5.10.0", "@vitest/eslint-plugin": "^1.6.19", "eslint": "^10.4.1", "eslint-config-prettier": "^10.1.8", diff --git a/packages/angularjs/src/index.ts b/packages/angularjs/src/index.ts index a46bf6a4..5deace4e 100644 --- a/packages/angularjs/src/index.ts +++ b/packages/angularjs/src/index.ts @@ -10,7 +10,11 @@ angular "$ExceptionlessClient", ($location: ng.ILocationService, $q: ng.IQService, $ExceptionlessClient: BrowserExceptionlessClient) => { return { - responseError: function responseError(response: ng.IHttpResponse<{ Message?: string }>) { + responseError: function responseError( + response: ng.IHttpResponse<{ + Message?: string; + }> + ) { if (response.status === 404) { void $ExceptionlessClient.submitNotFound(response.config.url); } else if (response.status !== 401) { @@ -48,7 +52,13 @@ angular function decorateRegularCall(property: string, logLevel: string) { const previousFn = $delegate[property]; return ($delegate[property] = (...args: string[]) => { - if ((angular as { mock?: unknown }).mock) { + if ( + ( + angular as { + mock?: unknown; + } + ).mock + ) { $delegate[property].logs = []; } diff --git a/packages/browser/src/plugins/BrowserErrorPlugin.ts b/packages/browser/src/plugins/BrowserErrorPlugin.ts index bb4e509c..dbfc33ca 100644 --- a/packages/browser/src/plugins/BrowserErrorPlugin.ts +++ b/packages/browser/src/plugins/BrowserErrorPlugin.ts @@ -59,7 +59,9 @@ export class BrowserErrorPlugin implements IEventPlugin { const items: ParameterInfo[] = []; for (const param of params) { - items.push({ name: param }); + items.push({ + name: param + }); } return items; diff --git a/packages/browser/src/plugins/BrowserGlobalHandlerPlugin.ts b/packages/browser/src/plugins/BrowserGlobalHandlerPlugin.ts index 10b1c279..c6cc54d8 100644 --- a/packages/browser/src/plugins/BrowserGlobalHandlerPlugin.ts +++ b/packages/browser/src/plugins/BrowserGlobalHandlerPlugin.ts @@ -2,7 +2,20 @@ import { ExceptionlessClient, IEventPlugin, PluginContext, toError } from "@exce declare let $: (document: Document) => { ajaxError: { - (document: (event: Event, xhr: { responseText: string; status: number }, settings: { data: unknown; url: string }, error: string) => void): void; + ( + document: ( + event: Event, + xhr: { + responseText: string; + status: number; + }, + settings: { + data: unknown; + url: string; + }, + error: string + ) => void + ): void; }; }; @@ -29,7 +42,13 @@ export class BrowserGlobalHandlerPlugin implements IEventPlugin { if (!(reason instanceof Error)) { try { // Check for reason in legacy CustomEvents (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) - const detailReason = (<{ detail?: { reason: string } }>event).detail?.reason; + const detailReason = (< + { + detail?: { + reason: string; + }; + } + >event).detail?.reason; if (detailReason) { reason = detailReason; } @@ -54,21 +73,34 @@ export class BrowserGlobalHandlerPlugin implements IEventPlugin { }); if (typeof $ !== "undefined" && $(document)) { - $(document).ajaxError((_: Event, xhr: { responseText: string; status: number }, settings: { data: unknown; url: string }, error: string) => { - if (xhr.status === 404) { - // TODO: Handle async - void this._client?.submitNotFound(settings.url); - } else if (xhr.status !== 401) { - // TODO: Handle async - void this._client - ?.createUnhandledException(toError(error), "JQuery.ajaxError") - .setSource(settings.url) - .setProperty("status", xhr.status) - .setProperty("request", settings.data) - .setProperty("response", xhr.responseText?.slice(0, 1024)) - .submit(); + $(document).ajaxError( + ( + _: Event, + xhr: { + responseText: string; + status: number; + }, + settings: { + data: unknown; + url: string; + }, + error: string + ) => { + if (xhr.status === 404) { + // TODO: Handle async + void this._client?.submitNotFound(settings.url); + } else if (xhr.status !== 401) { + // TODO: Handle async + void this._client + ?.createUnhandledException(toError(error), "JQuery.ajaxError") + .setSource(settings.url) + .setProperty("status", xhr.status) + .setProperty("request", settings.data) + .setProperty("response", xhr.responseText?.slice(0, 1024)) + .submit(); + } } - }); + ); } return Promise.resolve(); @@ -90,6 +122,7 @@ export class BrowserGlobalHandlerPlugin implements IEventPlugin { if (errorName) { name = errorName; } + if (errorMessage) { msg = errorMessage; } diff --git a/packages/browser/test/plugins/BrowserErrorPlugin.test.ts b/packages/browser/test/plugins/BrowserErrorPlugin.test.ts index b7ec9812..429f0225 100644 --- a/packages/browser/test/plugins/BrowserErrorPlugin.test.ts +++ b/packages/browser/test/plugins/BrowserErrorPlugin.test.ts @@ -56,7 +56,11 @@ describe("BrowserErrorPlugin", () => { const error = { someProperty: "Test" }; - await processError(new Error("Error With Cause", { cause: error })); + await processError( + new Error("Error With Cause", { + cause: error + }) + ); const additionalData = getAdditionalData(context.event); expect(additionalData).not.toBeNull(); expect(additionalData?.cause).toStrictEqual(error); diff --git a/packages/browser/test/plugins/BrowserIgnoreExtensionErrorsPlugin.test.ts b/packages/browser/test/plugins/BrowserIgnoreExtensionErrorsPlugin.test.ts index c9f4cf84..583324da 100644 --- a/packages/browser/test/plugins/BrowserIgnoreExtensionErrorsPlugin.test.ts +++ b/packages/browser/test/plugins/BrowserIgnoreExtensionErrorsPlugin.test.ts @@ -22,7 +22,13 @@ describe("BrowserIgnoreExtensionErrorsPlugin", () => { const eventContext = new EventContext(); eventContext.setException(error); - const context = new EventPluginContext(client, { type: "error" }, eventContext); + const context = new EventPluginContext( + client, + { + type: "error" + }, + eventContext + ); await plugin.run(context); return context; diff --git a/packages/core/src/EventBuilder.ts b/packages/core/src/EventBuilder.ts index 187c8dae..7645e812 100644 --- a/packages/core/src/EventBuilder.ts +++ b/packages/core/src/EventBuilder.ts @@ -87,7 +87,13 @@ export class EventBuilder { public setUserIdentity(identity: string): EventBuilder; public setUserIdentity(identity: string, name: string): EventBuilder; public setUserIdentity(userInfoOrIdentity: UserInfo | string, name?: string): EventBuilder { - const userInfo = typeof userInfoOrIdentity !== "string" ? userInfoOrIdentity : { identity: userInfoOrIdentity, name }; + const userInfo = + typeof userInfoOrIdentity !== "string" + ? userInfoOrIdentity + : { + identity: userInfoOrIdentity, + name + }; if (!userInfo || (!userInfo.identity && !userInfo.name)) { return this; } @@ -121,7 +127,9 @@ export class EventBuilder { */ public setManualStackingInfo(signatureData: Record, title?: string): EventBuilder { if (signatureData) { - const stack: ManualStackingInfo = { signature_data: signatureData }; + const stack: ManualStackingInfo = { + signature_data: signatureData + }; if (title) { stack.title = title; } @@ -139,7 +147,9 @@ export class EventBuilder { */ public setManualStackingKey(manualStackingKey: string, title?: string): EventBuilder { if (manualStackingKey) { - const data = { ManualStackingKey: manualStackingKey }; + const data = { + ManualStackingKey: manualStackingKey + }; this.setManualStackingInfo(data, title); } diff --git a/packages/core/src/ExceptionlessClient.ts b/packages/core/src/ExceptionlessClient.ts index 1f9495d6..e4590dca 100644 --- a/packages/core/src/ExceptionlessClient.ts +++ b/packages/core/src/ExceptionlessClient.ts @@ -195,7 +195,13 @@ export class ExceptionlessClient { } public createEvent(context?: EventContext): EventBuilder { - return new EventBuilder({ date: new Date() }, this, context); + return new EventBuilder( + { + date: new Date() + }, + this, + context + ); } /** @@ -264,7 +270,10 @@ export class ExceptionlessClient { return; } - const userDescription: UserDescription = { email_address: email, description }; + const userDescription: UserDescription = { + email_address: email, + description + }; const response = await this.config.services.submissionClient.submitUserDescription(referenceId, userDescription); if (!response.success) { this.config.services.log.error(`Failed to submit user email and description for event "${referenceId}": ${response.status} ${response.message}`); diff --git a/packages/core/src/Utils.ts b/packages/core/src/Utils.ts index 1d64be44..01fb4352 100644 --- a/packages/core/src/Utils.ts +++ b/packages/core/src/Utils.ts @@ -359,8 +359,18 @@ export function prune(value: unknown, depth: number = 10): unknown { } function normalizeValue(value: unknown): unknown { - function hasToJSONFunction(value: unknown): value is { toJSON: () => unknown } { - return value !== null && typeof value === "object" && typeof (value as { toJSON?: unknown }).toJSON === "function"; + function hasToJSONFunction(value: unknown): value is { + toJSON: () => unknown; + } { + return ( + value !== null && + typeof value === "object" && + typeof ( + value as { + toJSON?: unknown; + } + ).toJSON === "function" + ); } if (typeof value === "bigint") { @@ -469,7 +479,11 @@ export function prune(value: unknown, depth: number = 10): unknown { // Normalize the key so Symbols are converted to strings. const normalizedKey = normalizeValue(key) as NonSymbolPropertyKey; - const objectValue = (normalizedValue as { [index: PropertyKey]: unknown })[key]; + const objectValue = ( + normalizedValue as { + [index: PropertyKey]: unknown; + } + )[key]; result[normalizedKey] = pruneImpl(objectValue, maxDepth, currentDepth + 1, seen); } @@ -556,6 +570,10 @@ export function toError(errorOrMessage: unknown, defaultMessage = "Unknown Error */ export function allowProcessToExitWithoutWaitingForTimerOrInterval(timeoutOrIntervalId: ReturnType | undefined): void { if (typeof timeoutOrIntervalId === "object" && "unref" in timeoutOrIntervalId) { - (timeoutOrIntervalId as { unref: () => ReturnType }).unref(); + ( + timeoutOrIntervalId as { + unref: () => ReturnType; + } + ).unref(); } } diff --git a/packages/core/src/configuration/Configuration.ts b/packages/core/src/configuration/Configuration.ts index 1f852b07..8bb93381 100644 --- a/packages/core/src/configuration/Configuration.ts +++ b/packages/core/src/configuration/Configuration.ts @@ -378,7 +378,13 @@ export class Configuration { */ public addPlugin(name: string | undefined, priority: number, pluginAction: (context: EventPluginContext) => Promise): void; public addPlugin(pluginOrName: IEventPlugin | string | undefined, priority?: number, pluginAction?: (context: EventPluginContext) => Promise): void { - const plugin: IEventPlugin = pluginAction ? { name: pluginOrName as string, priority, run: pluginAction } : (pluginOrName as IEventPlugin); + const plugin: IEventPlugin = pluginAction + ? { + name: pluginOrName as string, + priority, + run: pluginAction + } + : (pluginOrName as IEventPlugin); if (!plugin || !(plugin.startup || plugin.run)) { this.services.log.error("Add plugin failed: startup or run method not defined"); @@ -442,7 +448,13 @@ export class Configuration { public setUserIdentity(identity: string): void; public setUserIdentity(identity: string, name: string): void; public setUserIdentity(userInfoOrIdentity: UserInfo | string, name?: string): void { - const userInfo: UserInfo = typeof userInfoOrIdentity !== "string" ? userInfoOrIdentity : { identity: userInfoOrIdentity, name }; + const userInfo: UserInfo = + typeof userInfoOrIdentity !== "string" + ? userInfoOrIdentity + : { + identity: userInfoOrIdentity, + name + }; const shouldRemove: boolean = !userInfo || (!userInfo.identity && !userInfo.name); if (shouldRemove) { diff --git a/packages/core/src/plugins/EventPluginContext.ts b/packages/core/src/plugins/EventPluginContext.ts index 754a679f..26454d6a 100644 --- a/packages/core/src/plugins/EventPluginContext.ts +++ b/packages/core/src/plugins/EventPluginContext.ts @@ -11,7 +11,9 @@ export class EventPluginContext { public event: Event, public eventContext: EventContext ) { - if (!this.eventContext) this.eventContext = new EventContext(); + if (!this.eventContext) { + this.eventContext = new EventContext(); + } } public get log(): ILog { diff --git a/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts b/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts index 6213171d..bd04a429 100644 --- a/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts +++ b/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts @@ -39,6 +39,7 @@ export class DuplicateCheckerPlugin implements IEventPlugin { if (error.message && error.message.length) { hash += (hash * 397) ^ getHashCode(error.message); } + if (error.stack_trace && error.stack_trace.length) { hash += (hash * 397) ^ getHashCode(JSON.stringify(error.stack_trace)); } @@ -70,7 +71,10 @@ export class DuplicateCheckerPlugin implements IEventPlugin { if (!context.cancelled) { context.log.trace(`Enqueueing event with hash: ${hashCode} to cache`); - this._processedHashCodes.push({ hash: hashCode, timestamp: now }); + this._processedHashCodes.push({ + hash: hashCode, + timestamp: now + }); // Only keep the last 50 recent errors. while (this._processedHashCodes.length > 50) { diff --git a/packages/core/src/queue/DefaultEventQueue.ts b/packages/core/src/queue/DefaultEventQueue.ts index c2c10a1c..15e813cb 100644 --- a/packages/core/src/queue/DefaultEventQueue.ts +++ b/packages/core/src/queue/DefaultEventQueue.ts @@ -265,7 +265,12 @@ export class DefaultEventQueue implements IEventQueue { for (const file of files) { if (file?.startsWith(this.QUEUE_PREFIX)) { const json = await storage.getItem(file); - if (json) this._queue.push({ file, event: JSON.parse(json) as Event }); + if (json) { + this._queue.push({ + file, + event: JSON.parse(json) as Event + }); + } } } } catch (ex) { @@ -280,7 +285,12 @@ export class DefaultEventQueue implements IEventQueue { const { log, storage } = this.config.services; const useStorage: boolean = this.config.usePersistedQueueStorage; - if (this._queue.push({ file, event }) > this.maxItems) { + if ( + this._queue.push({ + file, + event + }) > this.maxItems + ) { log.trace("Removing oldest queue entry: maxItems exceeded"); const item = this._queue.shift(); if (useStorage && item) { diff --git a/packages/core/src/storage/InMemoryStorage.ts b/packages/core/src/storage/InMemoryStorage.ts index 70a969d1..dfbfe294 100644 --- a/packages/core/src/storage/InMemoryStorage.ts +++ b/packages/core/src/storage/InMemoryStorage.ts @@ -18,11 +18,15 @@ export class InMemoryStorage implements IStorage { } public async key(index: number): Promise { - if (index < 0) return Promise.resolve(null); + if (index < 0) { + return Promise.resolve(null); + } const keys = await this.keys(); - if (index > keys.length) return Promise.resolve(null); + if (index > keys.length) { + return Promise.resolve(null); + } const key = keys[index]; return Promise.resolve(key ? key : null); diff --git a/packages/core/test/DocumentationExamples.test.ts b/packages/core/test/DocumentationExamples.test.ts index 30f3dbc2..85d2589a 100644 --- a/packages/core/test/DocumentationExamples.test.ts +++ b/packages/core/test/DocumentationExamples.test.ts @@ -61,9 +61,15 @@ describe("documentation examples", () => { expect(builder.target.reference_id).toBe("order-12345678"); expect(builder.target.tags).toEqual(["orders", "Critical"]); expect(builder.target.geo).toBe("43.595089,-88.444602"); - expect(builder.target.data?.Order).toEqual({ id: "order-123", quoteId: 123 }); + expect(builder.target.data?.Order).toEqual({ + id: "order-123", + quoteId: 123 + }); expect(builder.target.data?.Quote).toBe(123); - expect(builder.target.data?.[KnownEventDataKeys.UserInfo]).toEqual({ identity: "user-123", name: "Jane Doe" }); + expect(builder.target.data?.[KnownEventDataKeys.UserInfo]).toEqual({ + identity: "user-123", + name: "Jane Doe" + }); expect(builder.target.data?.[KnownEventDataKeys.UserDescription]).toEqual({ email_address: "jane@example.com", description: "The submit button returned a blank page." diff --git a/packages/core/test/Utils.test.ts b/packages/core/test/Utils.test.ts index 305efe0d..30ad1e2b 100644 --- a/packages/core/test/Utils.test.ts +++ b/packages/core/test/Utils.test.ts @@ -21,38 +21,79 @@ describe("Utils", () => { describe("prune", () => { test("circular reference", () => { - type Circular = { property: string; circularRef?: Circular }; - const circular: Circular = { property: "string" }; + type Circular = { + property: string; + circularRef?: Circular; + }; + const circular: Circular = { + property: "string" + }; circular.circularRef = circular; - const expected = { property: "string", circularRef: undefined }; + const expected = { + property: "string", + circularRef: undefined + }; const actual = prune(circular); expect(actual).toStrictEqual(expected); }); test("circular array reference", () => { - type Circular = { property: string; circularRef?: Circular; list?: Circular[] }; - const circular: Circular = { property: "string" }; + type Circular = { + property: string; + circularRef?: Circular; + list?: Circular[]; + }; + const circular: Circular = { + property: "string" + }; circular.circularRef = circular; circular.list = [circular]; - const expected = { property: "string", circularRef: undefined, list: [undefined] }; + const expected = { + property: "string", + circularRef: undefined, + list: [undefined] + }; const actual = prune(circular); expect(actual).toStrictEqual(expected); }); test("array reference removes duplicated object references", () => { - type PropertyObject = { property: string }; - const propObject: PropertyObject = { property: "string" }; + type PropertyObject = { + property: string; + }; + const propObject: PropertyObject = { + property: "string" + }; - const expected = [{ property: "string" }, undefined]; + const expected = [ + { + property: "string" + }, + undefined + ]; const actual = prune([propObject, propObject]); expect(actual).toStrictEqual(expected); }); test("array cloned no object references", () => { - const expected = [{ property: "string" }, { property: "string" }]; - const actual = prune([{ property: "string" }, { property: "string" }]); + const expected = [ + { + property: "string" + }, + { + property: "string" + } + ]; + const actual = prune([ + { + property: "string" + }, + { + property: "string" + } + ]); expect(actual).toStrictEqual(expected); }); @@ -139,8 +180,25 @@ describe("Utils", () => { }); test("for Array", () => { - const expected = [{ a: undefined }, [undefined], 1]; - const actual = prune([{ a: { b: 2 } }, [[]], 1], 1); + const expected = [ + { + a: undefined + }, + [undefined], + 1 + ]; + const actual = prune( + [ + { + a: { + b: 2 + } + }, + [[]], + 1 + ], + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -158,7 +216,10 @@ describe("Utils", () => { test("for Error", () => { const error = new Error("error"); - const expected = { message: error.message, stack: error.stack }; + const expected = { + message: error.message, + stack: error.stack + }; const actual = prune(error, 1); expect(actual).toStrictEqual(expected); }); @@ -177,8 +238,28 @@ describe("Utils", () => { const actual = prune( new Map([ // NOTE: this value is lost due to being converted to ["[object Object]", { a: { b: 2 }, b: 1 }] - [{ id: 1 }, { a: { b: 2 }, b: 1 }], - [{ id: 2 }, { a2: { b2: 2 }, b2: 1 }], + [ + { + id: 1 + }, + { + a: { + b: 2 + }, + b: 1 + } + ], + [ + { + id: 2 + }, + { + a2: { + b2: 2 + }, + b2: 1 + } + ], ["string key", "string key"], [123, 123], [Symbol("symbol"), ["symbol key"]] @@ -190,8 +271,19 @@ describe("Utils", () => { }); test("for Object", () => { - const expected = { a: undefined, b: 1 }; - const actual = prune({ a: { b: 2 }, b: 1 }, 1); + const expected = { + a: undefined, + b: 1 + }; + const actual = prune( + { + a: { + b: 2 + }, + b: 1 + }, + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -202,8 +294,25 @@ describe("Utils", () => { }); test("for Set", () => { - const expected = [{ a: undefined, b: 1 }, 1]; - const actual = prune(new Set([{ a: { b: 2 }, b: 1 }, 1]), 1); + const expected = [ + { + a: undefined, + b: 1 + }, + 1 + ]; + const actual = prune( + new Set([ + { + a: { + b: 2 + }, + b: 1 + }, + 1 + ]), + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -214,17 +323,40 @@ describe("Utils", () => { }); test("for WeakMap", () => { - const actual = prune(new WeakMap([[{}, { a: { b: 2 } }]]), 2); + const actual = prune( + new WeakMap([ + [ + {}, + { + a: { + b: 2 + } + } + ] + ]), + 2 + ); expect(actual).toBeUndefined(); }); test("for WeakSet", () => { - const actual = prune(new WeakSet([{ a: { b: 2 } }]), 2); + const actual = prune( + new WeakSet([ + { + a: { + b: 2 + } + } + ]), + 2 + ); expect(actual).toBeUndefined(); }); test("should handle toJSON", () => { - const expected = { test: "test" }; + const expected = { + test: "test" + }; const actual = prune({ number: 1, toJSON() { @@ -259,11 +391,69 @@ describe("Utils", () => { } }; - expect(prune(value, 1)).toStrictEqual({ ao: undefined }); - expect(prune(value, 2)).toStrictEqual({ ao: { bo: undefined, ba: undefined, bn: 1 } }); - expect(prune(value, 3)).toStrictEqual({ ao: { bo: { cn: 1, co: undefined }, ba: [{ cn: 1, co: undefined }], bn: 1 } }); - expect(prune(value, 4)).toStrictEqual({ ao: { bo: { cn: 1, co: { do: undefined } }, ba: [{ cn: 1, co: { do: undefined } }], bn: 1 } }); - expect(prune(value, 5)).toStrictEqual({ ao: { bo: { cn: 1, co: { do: {} } }, ba: [{ cn: 1, co: { do: {} } }], bn: 1 } }); + expect(prune(value, 1)).toStrictEqual({ + ao: undefined + }); + expect(prune(value, 2)).toStrictEqual({ + ao: { + bo: undefined, + ba: undefined, + bn: 1 + } + }); + expect(prune(value, 3)).toStrictEqual({ + ao: { + bo: { + cn: 1, + co: undefined + }, + ba: [ + { + cn: 1, + co: undefined + } + ], + bn: 1 + } + }); + expect(prune(value, 4)).toStrictEqual({ + ao: { + bo: { + cn: 1, + co: { + do: undefined + } + }, + ba: [ + { + cn: 1, + co: { + do: undefined + } + } + ], + bn: 1 + } + }); + expect(prune(value, 5)).toStrictEqual({ + ao: { + bo: { + cn: 1, + co: { + do: {} + } + }, + ba: [ + { + cn: 1, + co: { + do: {} + } + } + ], + bn: 1 + } + }); }); test("should prune inherited properties", () => { @@ -318,22 +508,40 @@ describe("Utils", () => { }); test("circular reference", () => { - type Circular = { property: string; circularRef?: Circular }; - const circular: Circular = { property: "string" }; + type Circular = { + property: string; + circularRef?: Circular; + }; + const circular: Circular = { + property: "string" + }; circular.circularRef = circular; - const expected = JSON.stringify({ property: "string", circularRef: undefined }); + const expected = JSON.stringify({ + property: "string", + circularRef: undefined + }); const actual = stringify(circular); expect(actual).toStrictEqual(expected); }); test("circular array reference", () => { - type Circular = { property: string; circularRef?: Circular; list?: Circular[] }; - const circular: Circular = { property: "string" }; + type Circular = { + property: string; + circularRef?: Circular; + list?: Circular[]; + }; + const circular: Circular = { + property: "string" + }; circular.circularRef = circular; circular.list = [circular]; - const expected = JSON.stringify({ property: "string", circularRef: undefined, list: [undefined] }); + const expected = JSON.stringify({ + property: "string", + circularRef: undefined, + list: [undefined] + }); const actual = stringify(circular); expect(actual).toStrictEqual(expected); }); @@ -417,8 +625,26 @@ describe("Utils", () => { }); test("for Array", () => { - const expected = JSON.stringify([{ a: undefined }, [undefined], 1]); - const actual = stringify([{ a: { b: 2 } }, [[]], 1], [], 1); + const expected = JSON.stringify([ + { + a: undefined + }, + [undefined], + 1 + ]); + const actual = stringify( + [ + { + a: { + b: 2 + } + }, + [[]], + 1 + ], + [], + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -437,7 +663,10 @@ describe("Utils", () => { test("for Error", () => { const error = new Error("error"); - const expected = JSON.stringify({ stack: error.stack, message: error.message }); + const expected = JSON.stringify({ + stack: error.stack, + message: error.message + }); const actual = stringify(error, [], 1); expect(actual).toStrictEqual(expected); }); @@ -456,8 +685,28 @@ describe("Utils", () => { const actual = stringify( new Map([ // NOTE: this value is lost due to being converted to ["[object Object]", { a: { b: 2 }, b: 1 }] - [{ id: 1 }, { a: { b: 2 }, b: 1 }], - [{ id: 2 }, { a2: { b2: 2 }, b2: 1 }], + [ + { + id: 1 + }, + { + a: { + b: 2 + }, + b: 1 + } + ], + [ + { + id: 2 + }, + { + a2: { + b2: 2 + }, + b2: 1 + } + ], ["string key", "string key"], [123, 123], [Symbol("symbol"), ["symbol key"]] @@ -470,8 +719,20 @@ describe("Utils", () => { }); test("for Object", () => { - const expected = JSON.stringify({ a: undefined, b: 1 }); - const actual = stringify({ a: { b: 2 }, b: 1 }, [], 1); + const expected = JSON.stringify({ + a: undefined, + b: 1 + }); + const actual = stringify( + { + a: { + b: 2 + }, + b: 1 + }, + [], + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -482,8 +743,26 @@ describe("Utils", () => { }); test("for Set", () => { - const expected = JSON.stringify([{ a: undefined, b: 1 }, 1]); - const actual = stringify(new Set([{ a: { b: 2 }, b: 1 }, 1]), [], 1); + const expected = JSON.stringify([ + { + a: undefined, + b: 1 + }, + 1 + ]); + const actual = stringify( + new Set([ + { + a: { + b: 2 + }, + b: 1 + }, + 1 + ]), + [], + 1 + ); expect(actual).toStrictEqual(expected); }); @@ -494,12 +773,35 @@ describe("Utils", () => { }); test("for WeakMap", () => { - const actual = stringify(new WeakMap([[{}, { a: { b: 2 } }]]), [], 2); + const actual = stringify( + new WeakMap([ + [ + {}, + { + a: { + b: 2 + } + } + ] + ]), + [], + 2 + ); expect(actual).toBeUndefined(); }); test("for WeakSet", () => { - const actual = stringify(new WeakSet([{ a: { b: 2 } }]), [], 2); + const actual = stringify( + new WeakSet([ + { + a: { + b: 2 + } + } + ]), + [], + 2 + ); expect(actual).toBeUndefined(); }); }); @@ -540,13 +842,79 @@ describe("Utils", () => { } }; - expect(stringify(value, [], 1)).toStrictEqual(JSON.stringify({ ao: undefined })); - expect(stringify(value, [], 2)).toStrictEqual(JSON.stringify({ ao: { bo: undefined, ba: undefined, bn: 1 } })); - expect(stringify(value, [], 3)).toStrictEqual(JSON.stringify({ ao: { bo: { cn: 1, co: undefined }, ba: [{ cn: 1, co: undefined }], bn: 1 } })); + expect(stringify(value, [], 1)).toStrictEqual( + JSON.stringify({ + ao: undefined + }) + ); + expect(stringify(value, [], 2)).toStrictEqual( + JSON.stringify({ + ao: { + bo: undefined, + ba: undefined, + bn: 1 + } + }) + ); + expect(stringify(value, [], 3)).toStrictEqual( + JSON.stringify({ + ao: { + bo: { + cn: 1, + co: undefined + }, + ba: [ + { + cn: 1, + co: undefined + } + ], + bn: 1 + } + }) + ); expect(stringify(value, [], 4)).toStrictEqual( - JSON.stringify({ ao: { bo: { cn: 1, co: { do: undefined } }, ba: [{ cn: 1, co: { do: undefined } }], bn: 1 } }) + JSON.stringify({ + ao: { + bo: { + cn: 1, + co: { + do: undefined + } + }, + ba: [ + { + cn: 1, + co: { + do: undefined + } + } + ], + bn: 1 + } + }) + ); + expect(stringify(value, [], 5)).toStrictEqual( + JSON.stringify({ + ao: { + bo: { + cn: 1, + co: { + do: {} + } + }, + ba: [ + { + cn: 1, + co: { + do: {} + } + } + ], + bn: 1 + } + }) ); - expect(stringify(value, [], 5)).toStrictEqual(JSON.stringify({ ao: { bo: { cn: 1, co: { do: {} } }, ba: [{ cn: 1, co: { do: {} } }], bn: 1 } })); }); test("should serialize inherited properties", () => { @@ -589,26 +957,57 @@ describe("Utils", () => { test("*password", () => { expect(stringify(user, ["*password"])).toBe( - JSON.stringify({ id: 1, name: "Blake", passwordResetToken: "a reset token", myPasswordValue: "123456", customValue: "Password", value: {} }) + JSON.stringify({ + id: 1, + name: "Blake", + passwordResetToken: "a reset token", + myPasswordValue: "123456", + customValue: "Password", + value: {} + }) ); }); test("password*", () => { expect(stringify(user, ["password*"])).toBe( - JSON.stringify({ id: 1, name: "Blake", myPassword: "123456", myPasswordValue: "123456", customValue: "Password", value: {} }) + JSON.stringify({ + id: 1, + name: "Blake", + myPassword: "123456", + myPasswordValue: "123456", + customValue: "Password", + value: {} + }) ); }); test("*password*", () => { - expect(stringify(user, ["*password*"])).toBe(JSON.stringify({ id: 1, name: "Blake", customValue: "Password", value: {} })); + expect(stringify(user, ["*password*"])).toBe( + JSON.stringify({ + id: 1, + name: "Blake", + customValue: "Password", + value: {} + }) + ); }); test("*Password*", () => { - expect(stringify(user, ["*Password*"])).toBe(JSON.stringify({ id: 1, name: "Blake", customValue: "Password", value: {} })); + expect(stringify(user, ["*Password*"])).toBe( + JSON.stringify({ + id: 1, + name: "Blake", + customValue: "Password", + value: {} + }) + ); }); test("*Address", () => { - const event = { type: "usage", source: "about" }; + const event = { + type: "usage", + source: "about" + }; expect(stringify(event, ["*Address"])).toBe(JSON.stringify(event)); }); }); @@ -682,7 +1081,9 @@ describe("Utils", () => { Date: new Date(), number: 1, string: "string", - object: { a: 1 }, + object: { + a: 1 + }, array: [1] }; diff --git a/packages/core/test/configuration/Configuration.test.ts b/packages/core/test/configuration/Configuration.test.ts index 4c8335cb..fc2aec8e 100644 --- a/packages/core/test/configuration/Configuration.test.ts +++ b/packages/core/test/configuration/Configuration.test.ts @@ -95,7 +95,12 @@ describe("Configuration", () => { resolve(); }); - config.applyServerSettings({ settings: { someValue: "UNIT_TEST_API_KEY" }, version: 2 }); + config.applyServerSettings({ + settings: { + someValue: "UNIT_TEST_API_KEY" + }, + version: 2 + }); }); }); }); diff --git a/packages/core/test/plugins/default/ConfigurationDefaultsPlugin.test.ts b/packages/core/test/plugins/default/ConfigurationDefaultsPlugin.test.ts index b533db5b..90e948fa 100644 --- a/packages/core/test/plugins/default/ConfigurationDefaultsPlugin.test.ts +++ b/packages/core/test/plugins/default/ConfigurationDefaultsPlugin.test.ts @@ -33,7 +33,11 @@ describe("ConfigurationDefaultsPlugin", () => { client.config.addDataExclusions(...dataExclusions); } - const ev: Event = { type: "log", source: "test", data: {} }; + const ev: Event = { + type: "log", + source: "test", + data: {} + }; const context = new EventPluginContext(client, ev, new EventContext()); const plugin = new ConfigurationDefaultsPlugin(); @@ -57,7 +61,12 @@ describe("ConfigurationDefaultsPlugin", () => { const ev = await run(["*password*"]); expect(ev.data).toBeDefined(); - const expected = { id: 1, name: "Blake", customValue: "Password", value: {} }; + const expected = { + id: 1, + name: "Blake", + customValue: "Password", + value: {} + }; expect(ev.data && ev.data[userDataKey]).toStrictEqual(expected); }); }); diff --git a/packages/core/test/plugins/default/EventExclusionPlugin.test.ts b/packages/core/test/plugins/default/EventExclusionPlugin.test.ts index 121167e9..39bda149 100644 --- a/packages/core/test/plugins/default/EventExclusionPlugin.test.ts +++ b/packages/core/test/plugins/default/EventExclusionPlugin.test.ts @@ -19,7 +19,11 @@ describe("EventExclusionPlugin", () => { client.config.settings[settingKey] = settingValue as string; } - const ev: Event = { type: "log", source, data: {} }; + const ev: Event = { + type: "log", + source, + data: {} + }; if (ev.data && level) { ev.data[KnownEventDataKeys.Level] = level; } @@ -67,7 +71,11 @@ describe("EventExclusionPlugin", () => { client.config.settings[settingKey] = settingValue as string; } - const ev: Event = { type: "log", source, data: {} }; + const ev: Event = { + type: "log", + source, + data: {} + }; if (ev.data && level) { ev.data[KnownEventDataKeys.Level] = level; } @@ -89,7 +97,10 @@ describe("EventExclusionPlugin", () => { describe("should resolve null and undefined log source levels in reverse settings order", () => { const plugin = new EventExclusionPlugin(); - const settings: Record = { "@@log:": "info", "@@log:*": "debug" }; + const settings: Record = { + "@@log:": "info", + "@@log:*": "debug" + }; test(" (global min level: info)", () => expect(plugin.getMinLogLevel(settings, undefined)).toBe(2)); test(" (source min level: info)", () => expect(plugin.getMinLogLevel(settings, "")).toBe(2)); @@ -98,7 +109,10 @@ describe("EventExclusionPlugin", () => { describe("should resolve log source levels and respect settings order", () => { const plugin = new EventExclusionPlugin(); - const settings = { "@@log:*": "debug", "@@log:": "info" }; + const settings = { + "@@log:*": "debug", + "@@log:": "info" + }; test(" (source min level: info)", () => expect(plugin.getMinLogLevel(settings, "")).toBe(2)); test("* (global min level: debug)", () => expect(plugin.getMinLogLevel(settings, "*")).toBe(1)); @@ -162,7 +176,15 @@ describe("EventExclusionPlugin", () => { client.config.settings[settingKey] = settingValue as string; } - const context = new EventPluginContext(client, { type: type, source, data: {} }, new EventContext()); + const context = new EventPluginContext( + client, + { + type: type, + source, + data: {} + }, + new EventContext() + ); const plugin = new EventExclusionPlugin(); await plugin.run(context); diff --git a/packages/core/test/queue/DefaultEventQueue.test.ts b/packages/core/test/queue/DefaultEventQueue.test.ts index 71e853d4..71067093 100644 --- a/packages/core/test/queue/DefaultEventQueue.test.ts +++ b/packages/core/test/queue/DefaultEventQueue.test.ts @@ -24,19 +24,31 @@ describe("DefaultEventQueue", () => { }); test("should enqueue event", async () => { - const event: Event = { type: "log", reference_id: "123454321" }; + const event: Event = { + type: "log", + reference_id: "123454321" + }; await config.services.queue.enqueue(event); expect(await config.services.storage.length()).toBe(1); }); test("should process queue", async () => { - const event: Event = { type: "log", reference_id: "123454321" }; + const event: Event = { + type: "log", + reference_id: "123454321" + }; await config.services.queue.enqueue(event); expect(await config.services.storage.length()).toBe(1); await config.services.queue.process(); config.services.queue.onEventsPosted(async () => { - expect((config.services.queue as { _suspendProcessingUntil?: Date })._suspendProcessingUntil).toBeUndefined(); + expect( + ( + config.services.queue as { + _suspendProcessingUntil?: Date; + } + )._suspendProcessingUntil + ).toBeUndefined(); expect(await config.services.storage.length()).toBe(0); }); }); @@ -44,7 +56,10 @@ describe("DefaultEventQueue", () => { test("should discard event submission", async () => { await config.services.queue.suspendProcessing(1, true); - const event: Event = { type: "log", reference_id: "123454321" }; + const event: Event = { + type: "log", + reference_id: "123454321" + }; await config.services.queue.enqueue(event); expect(await config.services.storage.length()).toBe(0); }); @@ -52,19 +67,29 @@ describe("DefaultEventQueue", () => { test("should suspend processing", async () => { await config.services.queue.suspendProcessing(0.0001); - const event: Event = { type: "log", reference_id: "123454321" }; + const event: Event = { + type: "log", + reference_id: "123454321" + }; await config.services.queue.enqueue(event); expect(await config.services.storage.length()).toBe(1); await delay(25); - const suspendProcessingUntil = (config.services.queue as { _suspendProcessingUntil?: Date })._suspendProcessingUntil; + const suspendProcessingUntil = ( + config.services.queue as { + _suspendProcessingUntil?: Date; + } + )._suspendProcessingUntil; const expectedLength = suspendProcessingUntil ? 1 : 0; expect(await config.services.storage.length()).toBe(expectedLength); }); test("should respect max items", async () => { config.services.queue = new DefaultEventQueue(config, 1); - const event: Event = { type: "log", reference_id: "123454321" }; + const event: Event = { + type: "log", + reference_id: "123454321" + }; for (let index = 0; index < 2; index++) { await config.services.queue.enqueue(event); expect(await config.services.storage.length()).toBe(1); diff --git a/packages/core/test/submission/TestSubmissionClient.test.ts b/packages/core/test/submission/TestSubmissionClient.test.ts index b2cd823a..77d2844e 100644 --- a/packages/core/test/submission/TestSubmissionClient.test.ts +++ b/packages/core/test/submission/TestSubmissionClient.test.ts @@ -20,7 +20,13 @@ describe("TestSubmissionClient", () => { .fn<(url: string, options: FetchOptions) => Promise>>() .mockReturnValueOnce(Promise.resolve(new Response(202, "", NaN, NaN, undefined))); - const events: Event[] = [{ type: "log", message: "From js client", reference_id: "123454321" }]; + const events: Event[] = [ + { + type: "log", + message: "From js client", + reference_id: "123454321" + } + ]; const client = new TestSubmissionClient(config, apiFetchMock); await client.submitEvents(events); expect(apiFetchMock).toHaveBeenCalledTimes(1); @@ -80,7 +86,9 @@ describe("TestSubmissionClient", () => { body: JSON.stringify(description) }); expect(apiFetchMock.mock.calls[1][0]).toBe(`${config.serverUrl}/api/v2/projects/config?v=0`); - expect(apiFetchMock.mock.calls[1][1]).toEqual({ method: "GET" }); + expect(apiFetchMock.mock.calls[1][1]).toEqual({ + method: "GET" + }); }); test("should submit heartbeat", async () => { @@ -92,7 +100,9 @@ describe("TestSubmissionClient", () => { await client.submitHeartbeat("sessionId", true); expect(apiFetchMock).toHaveBeenCalledTimes(1); expect(apiFetchMock.mock.calls[0][0]).toBe(`${config.heartbeatServerUrl}/api/v2/events/session/heartbeat?id=sessionId&close=true`); - expect(apiFetchMock.mock.calls[0][1]).toEqual({ method: "GET" }); + expect(apiFetchMock.mock.calls[0][1]).toEqual({ + method: "GET" + }); }); test("should get project settings", async () => { @@ -104,6 +114,8 @@ describe("TestSubmissionClient", () => { await client.getSettings(0); expect(apiFetchMock).toHaveBeenCalledTimes(1); expect(apiFetchMock.mock.calls[0][0]).toBe(`${config.serverUrl}/api/v2/projects/config?v=0`); - expect(apiFetchMock.mock.calls[0][1]).toEqual({ method: "GET" }); + expect(apiFetchMock.mock.calls[0][1]).toEqual({ + method: "GET" + }); }); }); diff --git a/packages/node/src/storage/NodeDirectoryStorage.ts b/packages/node/src/storage/NodeDirectoryStorage.ts index 3cd1eace..075d8968 100644 --- a/packages/node/src/storage/NodeDirectoryStorage.ts +++ b/packages/node/src/storage/NodeDirectoryStorage.ts @@ -15,7 +15,9 @@ export class NodeDirectoryStorage implements IStorage { this.directory = resolve(directory); } - mkdirSync(this.directory, { recursive: true }); + mkdirSync(this.directory, { + recursive: true + }); } public async length(): Promise { diff --git a/packages/node/test/storage/NodeDirectoryStorage.test.ts b/packages/node/test/storage/NodeDirectoryStorage.test.ts index 15314e7e..93d729d6 100644 --- a/packages/node/test/storage/NodeDirectoryStorage.test.ts +++ b/packages/node/test/storage/NodeDirectoryStorage.test.ts @@ -9,8 +9,13 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); const directory: string = path.join(__dirname, "data"); function resetStorageDirectory() { - rmSync(directory, { recursive: true, force: true }); - mkdirSync(directory, { recursive: true }); + rmSync(directory, { + recursive: true, + force: true + }); + mkdirSync(directory, { + recursive: true + }); } describeStorage("NodeDirectoryStorage", (): IStorage => new NodeDirectoryStorage(directory), resetStorageDirectory, resetStorageDirectory); diff --git a/packages/react-native/src/ExceptionlessErrorBoundary.tsx b/packages/react-native/src/ExceptionlessErrorBoundary.tsx index a3843160..8795a33a 100644 --- a/packages/react-native/src/ExceptionlessErrorBoundary.tsx +++ b/packages/react-native/src/ExceptionlessErrorBoundary.tsx @@ -15,11 +15,15 @@ interface ErrorBoundaryState { export class ExceptionlessErrorBoundary extends Component, ErrorBoundaryState> { constructor(props: Readonly>) { super(props); - this.state = { hasError: false }; + this.state = { + hasError: false + }; } static getDerivedStateFromError(): ErrorBoundaryState { - return { hasError: true }; + return { + hasError: true + }; } async componentDidCatch(error: Error, errorInfo: ReactErrorInfo): Promise { diff --git a/packages/react-native/src/logging/CallbackLog.ts b/packages/react-native/src/logging/CallbackLog.ts index 1d869ff8..2c6af9bb 100644 --- a/packages/react-native/src/logging/CallbackLog.ts +++ b/packages/react-native/src/logging/CallbackLog.ts @@ -46,7 +46,11 @@ export class CallbackLog implements ILog { } private _emit(level: LogEntry["level"], message: string): void { - const entry: LogEntry = { timestamp: new Date(), level, message }; + const entry: LogEntry = { + timestamp: new Date(), + level, + message + }; for (const cb of this._callbacks) { try { cb(entry); diff --git a/packages/react-native/test/ReactNativeExceptionlessClient.test.ts b/packages/react-native/test/ReactNativeExceptionlessClient.test.ts index ce79d011..02a25acf 100644 --- a/packages/react-native/test/ReactNativeExceptionlessClient.test.ts +++ b/packages/react-native/test/ReactNativeExceptionlessClient.test.ts @@ -6,7 +6,10 @@ import { ReactNativeExceptionlessClient } from "../src/ReactNativeExceptionlessC describe("ReactNativeExceptionlessClient", () => { afterEach(async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); }); test("should configure documented react native plugins and persisted queue storage", async () => { @@ -48,7 +51,10 @@ describe("ReactNativeExceptionlessClient", () => { test("should not configure native crash reporting on Android", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "android", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "android", + writable: true + }); const client = new ReactNativeExceptionlessClient(); await client.startup((config) => { diff --git a/packages/react-native/test/logging/CallbackLog.test.ts b/packages/react-native/test/logging/CallbackLog.test.ts index 2aef65f8..cca1e009 100644 --- a/packages/react-native/test/logging/CallbackLog.test.ts +++ b/packages/react-native/test/logging/CallbackLog.test.ts @@ -7,7 +7,12 @@ import type { LogEntry } from "../../src/logging/CallbackLog.js"; describe("CallbackLog", () => { test("should forward messages to inner logger", () => { - const inner = { trace: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }; + const inner = { + trace: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn() + }; const log = new CallbackLog(inner); log.trace("t"); @@ -50,7 +55,12 @@ describe("CallbackLog", () => { }); test("should not break if subscriber throws", () => { - const inner = { trace: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }; + const inner = { + trace: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn() + }; const log = new CallbackLog(inner); const good: LogEntry[] = []; diff --git a/packages/react-native/test/plugins/NativeCrashPlugin.test.ts b/packages/react-native/test/plugins/NativeCrashPlugin.test.ts index 56474d8b..6e7c2e77 100644 --- a/packages/react-native/test/plugins/NativeCrashPlugin.test.ts +++ b/packages/react-native/test/plugins/NativeCrashPlugin.test.ts @@ -36,36 +36,67 @@ describe("NativeCrashPlugin", () => { test("should be a no-op on non-iOS platforms", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "web", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "web", + writable: true + }); - await expect(plugin.startup({ client, log: client.config.services.log })).resolves.toBeUndefined(); + await expect( + plugin.startup({ + client, + log: client.config.services.log + }) + ).resolves.toBeUndefined(); - Object.defineProperty(Platform, "OS", { value: "android", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "android", + writable: true + }); const warnSpy = vi.spyOn(client.config.services.log, "warn"); - await expect(plugin.startup({ client, log: client.config.services.log })).resolves.toBeUndefined(); + await expect( + plugin.startup({ + client, + log: client.config.services.log + }) + ).resolves.toBeUndefined(); expect(warnSpy).not.toHaveBeenCalled(); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); }); test("should log warning when native module is unavailable on iOS", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); const warnSpy = vi.spyOn(client.config.services.log, "warn"); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Native crash reporter module not available")); warnSpy.mockRestore(); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); }); test("should submit pending native crash reports and clear them after successful submission", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); const report = createCrashReport(); const nativeModule = createNativeModule([report]); @@ -78,7 +109,10 @@ describe("NativeCrashPlugin", () => { submit } as never); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); expect(nativeModule.install).toHaveBeenCalledOnce(); expect(nativeModule.hasPendingCrashReport).toHaveBeenCalledOnce(); @@ -108,7 +142,10 @@ describe("NativeCrashPlugin", () => { test("should not clear pending native crash reports when a pending marker returns no reports", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); const nativeModule = createNativeModule([]); nativeModuleMock.current = nativeModule; @@ -116,7 +153,10 @@ describe("NativeCrashPlugin", () => { const warnSpy = vi.spyOn(client.config.services.log, "warn"); const createUnhandledException = vi.spyOn(client, "createUnhandledException"); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); expect(nativeModule.install).toHaveBeenCalledOnce(); expect(nativeModule.hasPendingCrashReport).toHaveBeenCalledOnce(); diff --git a/packages/react-native/test/plugins/ReactNativeEnvironmentInfoPlugin.test.ts b/packages/react-native/test/plugins/ReactNativeEnvironmentInfoPlugin.test.ts index 11ede5c3..6ad1e049 100644 --- a/packages/react-native/test/plugins/ReactNativeEnvironmentInfoPlugin.test.ts +++ b/packages/react-native/test/plugins/ReactNativeEnvironmentInfoPlugin.test.ts @@ -7,8 +7,14 @@ import { ReactNativeEnvironmentInfoPlugin } from "../../src/plugins/ReactNativeE describe("ReactNativeEnvironmentInfoPlugin", () => { afterEach(async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); - Object.defineProperty(Platform, "Version", { value: "18.0", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); + Object.defineProperty(Platform, "Version", { + value: "18.0", + writable: true + }); Object.defineProperty(Platform, "constants", { value: { interfaceIdiom: "phone", @@ -46,8 +52,14 @@ describe("ReactNativeEnvironmentInfoPlugin", () => { test("should add Android environment info without requiring Expo modules", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "android", writable: true }); - Object.defineProperty(Platform, "Version", { value: 35, writable: true }); + Object.defineProperty(Platform, "OS", { + value: "android", + writable: true + }); + Object.defineProperty(Platform, "Version", { + value: 35, + writable: true + }); Object.defineProperty(Platform, "constants", { value: { Brand: "google", @@ -86,7 +98,9 @@ describe("ReactNativeEnvironmentInfoPlugin", () => { test("should not overwrite existing environment info", async () => { const plugin = new ReactNativeEnvironmentInfoPlugin(); - const existing: EnvironmentInfo = { o_s_name: "custom" }; + const existing: EnvironmentInfo = { + o_s_name: "custom" + }; const context = createContext(existing); await plugin.run(context); @@ -99,7 +113,11 @@ function createContext(environmentInfo?: EnvironmentInfo): EventPluginContext { return new EventPluginContext( new ExceptionlessClient(), { - data: environmentInfo ? { [KnownEventDataKeys.EnvironmentInfo]: environmentInfo } : {} + data: environmentInfo + ? { + [KnownEventDataKeys.EnvironmentInfo]: environmentInfo + } + : {} }, new EventContext() ); diff --git a/packages/react-native/test/plugins/ReactNativeErrorPlugin.test.ts b/packages/react-native/test/plugins/ReactNativeErrorPlugin.test.ts index def55f86..3caa41cf 100644 --- a/packages/react-native/test/plugins/ReactNativeErrorPlugin.test.ts +++ b/packages/react-native/test/plugins/ReactNativeErrorPlugin.test.ts @@ -15,7 +15,13 @@ describe("ReactNativeErrorPlugin", () => { beforeEach(() => { plugin = new ReactNativeErrorPlugin(); - context = new EventPluginContext(new ExceptionlessClient(), { data: {} }, new EventContext()); + context = new EventPluginContext( + new ExceptionlessClient(), + { + data: {} + }, + new EventContext() + ); }); test("should parse Hermes Metro stack frames into error info", async () => { diff --git a/packages/react-native/test/plugins/ReactNativeGlobalHandlerPlugin.test.ts b/packages/react-native/test/plugins/ReactNativeGlobalHandlerPlugin.test.ts index 70b192d9..c668beb0 100644 --- a/packages/react-native/test/plugins/ReactNativeGlobalHandlerPlugin.test.ts +++ b/packages/react-native/test/plugins/ReactNativeGlobalHandlerPlugin.test.ts @@ -23,7 +23,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { afterEach(async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); console.error = originalConsoleError; delete globalWithHandlers.ErrorUtils; delete globalWithHandlers.RN$handleException; @@ -40,10 +43,16 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should setup web handlers on web platform", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "web", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "web", + writable: true + }); const addEventListenerSpy = vi.spyOn(window, "addEventListener"); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); expect(addEventListenerSpy).toHaveBeenCalledWith("error", expect.any(Function)); expect(addEventListenerSpy).toHaveBeenCalledWith("unhandledrejection", expect.any(Function)); @@ -51,11 +60,20 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should not register handlers twice", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "web", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "web", + writable: true + }); const addEventListenerSpy = vi.spyOn(window, "addEventListener"); - await plugin.startup({ client, log: client.config.services.log }); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); + await plugin.startup({ + client, + log: client.config.services.log + }); const errorCalls = addEventListenerSpy.mock.calls.filter(([event]) => event === "error"); expect(errorCalls).toHaveLength(1); @@ -63,7 +81,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should preserve existing native ErrorUtils handler", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); let registeredHandler: ((error: Error, isFatal?: boolean) => void) | undefined; const previousHandler = vi.fn(); @@ -75,7 +96,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { }; const submitSpy = vi.spyOn(client, "submitUnhandledException").mockResolvedValue(undefined as never); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); const error = new Error("Unhandled native error"); registeredHandler?.(error, true); @@ -86,7 +110,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should capture React Native promise rejections without swallowing the native handler", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); globalWithHandlers.ErrorUtils = { getGlobalHandler: vi.fn(() => vi.fn()), @@ -96,10 +123,15 @@ describe("ReactNativeGlobalHandlerPlugin", () => { globalWithHandlers.RN$handleException = previousNativeHandler; const submitSpy = vi.spyOn(client, "submitUnhandledException").mockResolvedValue(undefined as never); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); const rejection = new Error("Rejected from async work"); - const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { cause: rejection }); + const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { + cause: rejection + }); const handled = globalWithHandlers.RN$handleException?.(reactNativeError, false, true); expect(submitSpy).toHaveBeenCalledWith(rejection, "ReactNative.promiseRejectionTracking"); @@ -109,7 +141,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should wrap configurable React Native promise rejection handler when it is read-only", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); globalWithHandlers.ErrorUtils = { getGlobalHandler: vi.fn(() => vi.fn()), @@ -123,10 +158,17 @@ describe("ReactNativeGlobalHandlerPlugin", () => { }); const submitSpy = vi.spyOn(client, "submitUnhandledException").mockResolvedValue(undefined as never); - await expect(plugin.startup({ client, log: client.config.services.log })).resolves.toBeUndefined(); + await expect( + plugin.startup({ + client, + log: client.config.services.log + }) + ).resolves.toBeUndefined(); const rejection = new Error("Rejected from async work"); - const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { cause: rejection }); + const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { + cause: rejection + }); const handled = globalWithHandlers.RN$handleException?.(reactNativeError, false, true); expect(submitSpy).toHaveBeenCalledWith(rejection, "ReactNative.promiseRejectionTracking"); @@ -136,7 +178,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should capture React Native promise rejections through console fallback when native handler cannot be wrapped", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); globalWithHandlers.ErrorUtils = { getGlobalHandler: vi.fn(() => vi.fn()), @@ -160,10 +205,15 @@ describe("ReactNativeGlobalHandlerPlugin", () => { const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); const submitSpy = vi.spyOn(client, "submitUnhandledException").mockResolvedValue(undefined as never); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); const rejection = new Error("Rejected from async work"); - const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { cause: rejection }); + const reactNativeError = new Error('Uncaught (in promise, id: 0): "Error: Rejected from async work"', { + cause: rejection + }); console.error(reactNativeError); expect(submitSpy).toHaveBeenCalledWith(rejection, "ReactNative.promiseRejectionTracking"); @@ -172,7 +222,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { test("should not capture every React Native nonfatal exception as a promise rejection", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); globalWithHandlers.ErrorUtils = { getGlobalHandler: vi.fn(() => vi.fn()), @@ -180,7 +233,10 @@ describe("ReactNativeGlobalHandlerPlugin", () => { }; const submitSpy = vi.spyOn(client, "submitUnhandledException").mockResolvedValue(undefined as never); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); globalWithHandlers.RN$handleException?.(new Error("console.error from React Native"), false, false); diff --git a/packages/react-native/test/plugins/ReactNativeLifeCyclePlugin.test.ts b/packages/react-native/test/plugins/ReactNativeLifeCyclePlugin.test.ts index a51440a3..b5c2be91 100644 --- a/packages/react-native/test/plugins/ReactNativeLifeCyclePlugin.test.ts +++ b/packages/react-native/test/plugins/ReactNativeLifeCyclePlugin.test.ts @@ -23,41 +23,70 @@ describe("ReactNativeLifeCyclePlugin", () => { test("should setup web listeners on web platform", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "web", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "web", + writable: true + }); const addEventListenerSpy = vi.spyOn(globalThis, "addEventListener"); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); expect(addEventListenerSpy).toHaveBeenCalledWith("beforeunload", expect.any(Function)); addEventListenerSpy.mockRestore(); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); }); test("should not setup handlers twice on repeated startup", async () => { const { Platform } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "web", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "web", + writable: true + }); const addEventListenerSpy = vi.spyOn(globalThis, "addEventListener"); - await plugin.startup({ client, log: client.config.services.log }); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); + await plugin.startup({ + client, + log: client.config.services.log + }); const beforeUnloadCalls = addEventListenerSpy.mock.calls.filter(([event]) => event === "beforeunload"); expect(beforeUnloadCalls).toHaveLength(1); addEventListenerSpy.mockRestore(); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); }); test("should keep lifecycle listeners active when client suspends", async () => { const { Platform } = await import("react-native"); const { AppState } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); - - const subscription = { remove: vi.fn() }; + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); + + const subscription = { + remove: vi.fn() + }; const addEventListenerSpy = vi.spyOn(AppState, "addEventListener").mockReturnValue(subscription); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); await expect(plugin.suspend()).resolves.toBeUndefined(); expect(subscription.remove).not.toHaveBeenCalled(); @@ -67,14 +96,20 @@ describe("ReactNativeLifeCyclePlugin", () => { test("should end native session only when app enters background", async () => { const { Platform } = await import("react-native"); const { AppState } = await import("react-native"); - Object.defineProperty(Platform, "OS", { value: "ios", writable: true }); + Object.defineProperty(Platform, "OS", { + value: "ios", + writable: true + }); client.config.useSessions(); const addEventListenerSpy = vi.spyOn(AppState, "addEventListener"); const submitSessionEndSpy = vi.spyOn(client, "submitSessionEnd").mockResolvedValue("reference-id"); const suspendSpy = vi.spyOn(client, "suspend").mockResolvedValue(); - await plugin.startup({ client, log: client.config.services.log }); + await plugin.startup({ + client, + log: client.config.services.log + }); const appStateHandler = addEventListenerSpy.mock.calls[0]?.[1]; expect(appStateHandler).toBeDefined(); diff --git a/packages/react-native/test/storage/AsyncStorageProvider.test.ts b/packages/react-native/test/storage/AsyncStorageProvider.test.ts index 45da46dd..bc8ed17f 100644 --- a/packages/react-native/test/storage/AsyncStorageProvider.test.ts +++ b/packages/react-native/test/storage/AsyncStorageProvider.test.ts @@ -8,13 +8,17 @@ describe("AsyncStorageProvider", () => { let storage: AsyncStorageProvider; beforeEach(() => { - const mock = AsyncStorage as unknown as { __reset: () => void }; + const mock = AsyncStorage as unknown as { + __reset: () => void; + }; mock.__reset(); storage = new AsyncStorageProvider(); }); afterEach(() => { - const mock = AsyncStorage as unknown as { __reset: () => void }; + const mock = AsyncStorage as unknown as { + __reset: () => void; + }; mock.__reset(); }); diff --git a/packages/react/src/ExceptionlessErrorBoundary.tsx b/packages/react/src/ExceptionlessErrorBoundary.tsx index 41058d86..a6986f5f 100644 --- a/packages/react/src/ExceptionlessErrorBoundary.tsx +++ b/packages/react/src/ExceptionlessErrorBoundary.tsx @@ -14,11 +14,15 @@ interface ErrorState { export class ExceptionlessErrorBoundary extends Component, ErrorState> { constructor(props: Readonly>) { super(props); - this.state = { hasError: false }; + this.state = { + hasError: false + }; } static getDerivedStateFromError(): ErrorState { - return { hasError: true }; + return { + hasError: true + }; } async componentDidCatch(error: Error, errorInfo: ReactErrorInfo): Promise { diff --git a/scripts/eslint-rules.test.mjs b/scripts/eslint-rules.test.mjs new file mode 100644 index 00000000..466ed0de --- /dev/null +++ b/scripts/eslint-rules.test.mjs @@ -0,0 +1,73 @@ +import { ESLint } from "eslint"; +import { expect, test } from "vitest"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const repositoryRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const eslintConfigPath = path.join(repositoryRoot, "eslint.config.mjs"); + +async function lint(source) { + const eslint = new ESLint({ + cwd: repositoryRoot, + overrideConfigFile: eslintConfigPath + }); + + const [result] = await eslint.lintText(source, { + filePath: "scripts/eslint-rule-fixture.mjs" + }); + + return result; +} + +test("accepts the required control-flow and object formatting", async () => { + const result = await lint(` +if (process.env.NODE_ENV) { + console.log("enabled"); +} + +while (process.env.NODE_ENV) { + console.log("pending"); +} + +const result = { + key: "value" +}; + +if (result.key) { + console.log(result.key); +} +`); + + expect(result.messages).toHaveLength(0); +}); + +test("requires curly braces for control-flow statements", async () => { + const result = await lint('if (process.env.NODE_ENV) console.log("enabled");'); + + expect(result.messages.map((message) => message.ruleId)).toContain("curly"); +}); + +test("requires object braces to wrap their contents", async () => { + const result = await lint('const result = { key: "value" };'); + + expect(result.messages.map((message) => message.ruleId)).toContain("@stylistic/object-curly-newline"); +}); + +test("requires block contents and closing braces to use separate lines", async () => { + const result = await lint('if (process.env.NODE_ENV) { console.log("enabled"); }'); + + expect(result.messages.map((message) => message.ruleId)).toContain("@stylistic/brace-style"); +}); + +test("requires blank lines between consecutive control-flow blocks", async () => { + const result = await lint(` +if (process.env.NODE_ENV) { + console.log("enabled"); +} +while (process.env.NODE_ENV) { + console.log("pending"); +} +`); + + expect(result.messages.map((message) => message.ruleId)).toContain("padding-line-between-statements"); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 09130b02..2518e854 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -106,6 +106,14 @@ export default defineConfig({ root: "example/nextjs", environment: "node" } + }, + { + test: { + name: "scripts", + root: ".", + environment: "node", + include: ["scripts/**/*.test.mjs"] + } } ] }