From a82c0f6fd2a60525a2242922d68f2e4f9ccd173b Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:54:24 -0800 Subject: [PATCH 01/16] feat: save consent in localStorage for browsers --- src/BrowserMetrics.ts | 7 ++++++- src/BrowserStorageProvider.ts | 28 ++++++++++++++++++++++++++++ src/MetricsProvider.ts | 34 ++++++++++++++++++++++++++++++---- src/NodeMetrics.ts | 5 ++++- src/types/index.d.ts | 5 +++++ 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/BrowserStorageProvider.ts diff --git a/src/BrowserMetrics.ts b/src/BrowserMetrics.ts index 09fa2f0..3f2afe6 100644 --- a/src/BrowserMetrics.ts +++ b/src/BrowserMetrics.ts @@ -1,10 +1,15 @@ import Countly from 'countly-sdk-web' import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsProvider.js' +import { BrowserStorageProvider } from './BrowserStorageProvider.js' export class BrowserMetricsProvider extends MetricsProvider { constructor (args: Omit, 'metricsService'>) { - super({ ...args, metricsService: Countly }) + super({ + metricsService: Countly, + storageProvider: BrowserStorageProvider, + ...args + }) } } diff --git a/src/BrowserStorageProvider.ts b/src/BrowserStorageProvider.ts new file mode 100644 index 0000000..222ea89 --- /dev/null +++ b/src/BrowserStorageProvider.ts @@ -0,0 +1,28 @@ +import type { StorageProvider } from './types/index.js' + +export const BrowserStorageProvider: StorageProvider = { + setStore: (consentArray) => { + try { + const jsonString = JSON.stringify(consentArray) + window.localStorage.setItem('@ipfs-shipyard/ignite-metrics:consent', jsonString) + } catch (err) { + // eslint-disable-next-line no-console + console.error(err) + } + }, + getStore: () => { + try { + const jsonString = window.localStorage.getItem('@ipfs-shipyard/ignite-metrics:consent') + if (jsonString != null) { + return JSON.parse(jsonString) + } + } catch (err) { + // eslint-disable-next-line no-console + console.error(err) + } + /** + * Return minimal consent if there is nothing in the store. + */ + return ['minimal'] + } +} diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index c1d3150..03ff80f 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -1,16 +1,17 @@ import { COUNTLY_API_URL } from './config.js' import type { metricFeatures, CountlyWebSdk } from 'countly-sdk-web' import type { CountlyNodeSdk } from 'countly-sdk-nodejs' -import type { consentTypes, consentTypesExceptAll } from './types/index.js' +import type { consentTypes, consentTypesExceptAll, StorageProvider } from './types/index.js' export interface MetricsProviderConstructorOptions { appKey: string url?: string autoTrack?: boolean metricsService: T + storageProvider?: StorageProvider | null } -export default class MetricsProvider { +export default class MetricsProvider { private readonly groupedFeatures: Record = this.mapAllEvents({ minimal: ['sessions', 'views', 'events'], performance: ['crashes', 'apm'], @@ -22,9 +23,17 @@ export default class MetricsProvider { private sessionStarted: boolean = false private readonly _consentGranted: Set = new Set() private readonly metricsService: T - - constructor ({ autoTrack = true, url = COUNTLY_API_URL, appKey, metricsService }: MetricsProviderConstructorOptions) { + private readonly storageProvider: StorageProvider | null + + constructor ({ + autoTrack = true, + url = COUNTLY_API_URL, + appKey, + metricsService, + storageProvider + }: MetricsProviderConstructorOptions) { this.metricsService = metricsService + this.storageProvider = storageProvider ?? null this.metricsService.init({ app_key: appKey, url, @@ -36,6 +45,8 @@ export default class MetricsProvider { if (autoTrack) { this.setupAutoTrack() } + + this.getConsentStore().forEach(this.addConsent.bind(this)) } mapAllEvents (eventMap: Record): Record { @@ -66,6 +77,7 @@ export default class MetricsProvider { } consent.forEach(c => this._consentGranted.add(c)) this.metricsService.add_consent(consent) + this.setConsentStore() } removeConsent (consent: consentTypes | consentTypes[]): void { @@ -74,6 +86,20 @@ export default class MetricsProvider { } consent.forEach(c => this._consentGranted.delete(c)) this.metricsService.remove_consent(consent, true) + this.setConsentStore() + } + + private setConsentStore (): void { + if (this.storageProvider != null) { + this.storageProvider.setStore(Array.from(this._consentGranted)) + } + } + + private getConsentStore (): consentTypes[] { + if (this.storageProvider != null) { + return this.storageProvider.getStore() + } + return [] } checkConsent (consent: consentTypes | metricFeatures): boolean { diff --git a/src/NodeMetrics.ts b/src/NodeMetrics.ts index 46a71e7..d7de2e0 100644 --- a/src/NodeMetrics.ts +++ b/src/NodeMetrics.ts @@ -4,7 +4,10 @@ import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsPro export class NodeMetricsProvider extends MetricsProvider { constructor (args: Omit, 'metricsService'>) { - super({ ...args, metricsService: Countly }) + super({ + metricsService: Countly, + ...args + }) } } diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 99beef8..c5ae797 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,3 +1,8 @@ export type consentTypesExceptAll = 'minimal' | 'performance' | 'ux' | 'feedback' | 'location' export type consentTypes = 'all' | consentTypesExceptAll + +export interface StorageProvider { + setStore: (values: consentTypes[]) => void + getStore: () => consentTypes[] +} From d8d6326d51d5b5f023a2470ffb34fdc1b369cb09 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Wed, 18 Jan 2023 17:23:36 -0800 Subject: [PATCH 02/16] chore: fix linter --- src/MetricsProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index a64421c..927c34a 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -47,7 +47,7 @@ export default class MetricsProvider { this.metricsService.init(serviceConfig) this.metricsService.group_features(this.groupedFeatures) - if (autoTrack === true) { + if (autoTrack) { this.setupAutoTrack() } From 2cd942ecb3175790af2b81daba9e71a72a1537b7 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:09:59 -0800 Subject: [PATCH 03/16] Update src/MetricsProvider.ts Co-authored-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- src/MetricsProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index 927c34a..4cc7b44 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -51,7 +51,7 @@ export default class MetricsProvider { this.setupAutoTrack() } - this.getConsentStore().forEach(this.addConsent.bind(this)) + this.getConsentStore().forEach(consent => this.addConsent(consent)) } mapAllEvents (eventMap: Record): Record { From 20920d647ffe2e8f18a4e1c47a3b83617ce808b2 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:11:05 -0800 Subject: [PATCH 04/16] Update src/MetricsProvider.ts Co-authored-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- src/MetricsProvider.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index 4cc7b44..fa3f317 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -101,10 +101,7 @@ export default class MetricsProvider { } private getConsentStore (): consentTypes[] { - if (this.storageProvider != null) { - return this.storageProvider.getStore() - } - return [] + return this.storageProvider?.getStore() ?? [] } checkConsent (consent: consentTypes | metricFeatures): boolean { From d14d52a098ef0000d3a560e50aea2f093e7b5a7f Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 12:48:23 -0800 Subject: [PATCH 05/16] chore: fix lint --- src/MetricsProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index fa3f317..a5b3766 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -51,7 +51,7 @@ export default class MetricsProvider { this.setupAutoTrack() } - this.getConsentStore().forEach(consent => this.addConsent(consent)) + this.getConsentStore().forEach((consent) => { this.addConsent(consent) }) } mapAllEvents (eventMap: Record): Record { From 1e6889853df70bbe8cf4696666888c88ba99d613 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 12:49:25 -0800 Subject: [PATCH 06/16] fix: use globalThis instead of window --- src/BrowserStorageProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BrowserStorageProvider.ts b/src/BrowserStorageProvider.ts index 222ea89..d97cc4e 100644 --- a/src/BrowserStorageProvider.ts +++ b/src/BrowserStorageProvider.ts @@ -4,7 +4,7 @@ export const BrowserStorageProvider: StorageProvider = { setStore: (consentArray) => { try { const jsonString = JSON.stringify(consentArray) - window.localStorage.setItem('@ipfs-shipyard/ignite-metrics:consent', jsonString) + globalThis.localStorage.setItem('@ipfs-shipyard/ignite-metrics:consent', jsonString) } catch (err) { // eslint-disable-next-line no-console console.error(err) @@ -12,7 +12,7 @@ export const BrowserStorageProvider: StorageProvider = { }, getStore: () => { try { - const jsonString = window.localStorage.getItem('@ipfs-shipyard/ignite-metrics:consent') + const jsonString = globalThis.localStorage.getItem('@ipfs-shipyard/ignite-metrics:consent') if (jsonString != null) { return JSON.parse(jsonString) } From cc19b56f9c6b43d5b7c2535615f366fbe806911b Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 13:13:22 -0800 Subject: [PATCH 07/16] feat: create StorageProvider class --- src/BrowserMetrics.ts | 2 +- src/BrowserStorageProvider.ts | 12 +++++++----- src/MetricsProvider.ts | 3 ++- src/types/index.d.ts | 5 ----- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/BrowserMetrics.ts b/src/BrowserMetrics.ts index 3f2afe6..3aba0cd 100644 --- a/src/BrowserMetrics.ts +++ b/src/BrowserMetrics.ts @@ -7,7 +7,7 @@ export class BrowserMetricsProvider extends MetricsProvider { constructor (args: Omit, 'metricsService'>) { super({ metricsService: Countly, - storageProvider: BrowserStorageProvider, + storageProvider: new BrowserStorageProvider(), ...args }) } diff --git a/src/BrowserStorageProvider.ts b/src/BrowserStorageProvider.ts index d97cc4e..e13bd4d 100644 --- a/src/BrowserStorageProvider.ts +++ b/src/BrowserStorageProvider.ts @@ -1,7 +1,8 @@ -import type { StorageProvider } from './types/index.js' +import type { consentTypes } from './types/index.js' +import type { StorageProvider } from './StorageProvider.js' -export const BrowserStorageProvider: StorageProvider = { - setStore: (consentArray) => { +export class BrowserStorageProvider implements StorageProvider { + setStore(consentArray: consentTypes[]) { try { const jsonString = JSON.stringify(consentArray) globalThis.localStorage.setItem('@ipfs-shipyard/ignite-metrics:consent', jsonString) @@ -9,8 +10,9 @@ export const BrowserStorageProvider: StorageProvider = { // eslint-disable-next-line no-console console.error(err) } - }, - getStore: () => { + } + + getStore(): consentTypes[] { try { const jsonString = globalThis.localStorage.getItem('@ipfs-shipyard/ignite-metrics:consent') if (jsonString != null) { diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index a5b3766..28003fb 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -2,7 +2,8 @@ import { COUNTLY_SETUP_DEFAULTS } from './config.js' import type { metricFeatures, CountlyWebSdk } from 'countly-sdk-web' import type { CountlyNodeSdk } from 'countly-sdk-nodejs' -import type { consentTypes, consentTypesExceptAll, StorageProvider } from './types/index.js' +import type { consentTypes, consentTypesExceptAll } from './types/index.js' +import type { StorageProvider } from './StorageProvider.js' export interface MetricsProviderConstructorOptions { appKey: string diff --git a/src/types/index.d.ts b/src/types/index.d.ts index c5ae797..99beef8 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,8 +1,3 @@ export type consentTypesExceptAll = 'minimal' | 'performance' | 'ux' | 'feedback' | 'location' export type consentTypes = 'all' | consentTypesExceptAll - -export interface StorageProvider { - setStore: (values: consentTypes[]) => void - getStore: () => consentTypes[] -} From 0263567d13549bd4b68d24c74e69f782e3676ab6 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 13:14:26 -0800 Subject: [PATCH 08/16] chore: enable testing --- .aegir.js | 4 +++- package-lock.json | 1 + package.json | 1 + tsconfig.json | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.aegir.js b/.aegir.js index e5d0e6c..55fe0b1 100644 --- a/.aegir.js +++ b/.aegir.js @@ -41,5 +41,7 @@ export default { }, bundlesizeMax: '44KB', }, - test: {} + test: { + build: true, + } } diff --git a/package-lock.json b/package-lock.json index 15a8832..4692b51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@storybook/manager-webpack5": "^6.5.15", "@storybook/react": "^6.5.15", "@storybook/testing-library": "^0.0.13", + "@types/mocha": "^10.0.1", "@types/react-dom": "^18.0.10", "@types/styled-components": "^5.1.26", "aegir": "^38.1.0", diff --git a/package.json b/package.json index 1cf5d7c..9611b14 100644 --- a/package.json +++ b/package.json @@ -173,6 +173,7 @@ "@storybook/manager-webpack5": "^6.5.15", "@storybook/react": "^6.5.15", "@storybook/testing-library": "^0.0.13", + "@types/mocha": "^10.0.1", "@types/react-dom": "^18.0.10", "@types/styled-components": "^5.1.26", "aegir": "^38.1.0", diff --git a/tsconfig.json b/tsconfig.json index 9f8b9b3..61b666f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { "jsx": "react", - "types": ["node"], + "types": ["node", "mocha"], "outDir": "dist", "target": "ES6", "allowSyntheticDefaultImports": true, @@ -13,6 +13,7 @@ }, "include": [ "src", + "test", "src/**/*.css" ], "exclude": [ From 84ee0e26f544bc020f8780773d0f59450f788f9a Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 15:08:07 -0800 Subject: [PATCH 09/16] fix: types and construction --- src/BrowserMetrics.ts | 2 +- src/MetricsProvider.ts | 19 +++++++------------ src/types/countlyNode.d.ts | 3 ++- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/BrowserMetrics.ts b/src/BrowserMetrics.ts index 3aba0cd..c766474 100644 --- a/src/BrowserMetrics.ts +++ b/src/BrowserMetrics.ts @@ -4,7 +4,7 @@ import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsPro import { BrowserStorageProvider } from './BrowserStorageProvider.js' export class BrowserMetricsProvider extends MetricsProvider { - constructor (args: Omit, 'metricsService'>) { + constructor (args: Omit, 'metricsService'> & Partial, 'metricsService'>>) { super({ metricsService: Countly, storageProvider: new BrowserStorageProvider(), diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index 28003fb..f9749c5 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -32,18 +32,12 @@ export default class MetricsProvider { private readonly storageProvider: StorageProvider | null constructor (config: MetricsProviderConstructorOptions) { - const serviceConfig = { + const { autoTrack, metricsService, storageProvider, ...serviceConfig } = { ...COUNTLY_SETUP_DEFAULTS, ...config } - const { appKey, autoTrack, metricsService, url, storageProvider } = serviceConfig this.metricsService = metricsService this.storageProvider = storageProvider ?? null - this.metricsService.init({ - app_key: appKey, - url, - require_consent: true - }) this.metricsService.init(serviceConfig) this.metricsService.group_features(this.groupedFeatures) @@ -67,13 +61,14 @@ export default class MetricsProvider { } setupAutoTrack (): void { - this.metricsService.track_clicks() + const webSdk = this.metricsService as CountlyWebSdk + webSdk.track_clicks?.() this.metricsService.track_errors() - this.metricsService.track_forms() - this.metricsService.track_links() + webSdk.track_forms?.() + webSdk.track_links?.() this.metricsService.track_pageview() - this.metricsService.track_scrolls() - this.metricsService.track_sessions() + webSdk.track_scrolls?.() + webSdk.track_sessions?.() this.metricsService.track_view() } diff --git a/src/types/countlyNode.d.ts b/src/types/countlyNode.d.ts index 2298de3..9350739 100644 --- a/src/types/countlyNode.d.ts +++ b/src/types/countlyNode.d.ts @@ -1,5 +1,6 @@ declare module 'countly-sdk-nodejs' { - export type CountlyNodeSdk = import('countly-sdk-web').CountlyWebSdk + type missingNodeSdkMethods = 'track_clicks' | 'track_forms' | 'track_links' | 'track_scrolls' | 'track_sessions' + export type CountlyNodeSdk = Omit const Countly: CountlyNodeSdk export type metricFeatures = import('countly-sdk-web').metricFeatures export default Countly From 8334d477ea5773b61c29fc034bce54d06c43250f Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 15:08:50 -0800 Subject: [PATCH 10/16] chore: add StorageProvider class --- src/StorageProvider.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/StorageProvider.ts diff --git a/src/StorageProvider.ts b/src/StorageProvider.ts new file mode 100644 index 0000000..53654e0 --- /dev/null +++ b/src/StorageProvider.ts @@ -0,0 +1,20 @@ +import type { consentTypes } from './types/index.js' + +export interface StorageProviderInterface { + setStore: (values: consentTypes[]) => void + getStore: () => consentTypes[] +} + +export class StorageProvider { + constructor(options: StorageProviderInterface) { + this.setStore = options.setStore ?? this.setStore + this.getStore = options.getStore ?? this.setStore + } + + setStore(values: consentTypes[]): void { + throw new Error('Method not implemented') + } + getStore(): consentTypes[] { + throw new Error('Method not implemented') + } +} From 3e9c2a7f36da2bd483b1bf4bcc68a7d0e8124979 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 15:46:43 -0800 Subject: [PATCH 11/16] fix: build artifacts dont use this --- src/MetricsProvider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index f9749c5..6b4febb 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -32,10 +32,11 @@ export default class MetricsProvider { private readonly storageProvider: StorageProvider | null constructor (config: MetricsProviderConstructorOptions) { - const { autoTrack, metricsService, storageProvider, ...serviceConfig } = { + const serviceConfig = { ...COUNTLY_SETUP_DEFAULTS, ...config } + const { autoTrack, metricsService, storageProvider } = serviceConfig this.metricsService = metricsService this.storageProvider = storageProvider ?? null From 57636b2607bd56c786989dac62574ce2cc4e6230 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:12:45 -0800 Subject: [PATCH 12/16] feat: add tests --- .aegir.js | 2 +- .eslintrc.json | 10 +- package-lock.json | 642 +++++++++++++++++++++++--- package.json | 11 +- src/BrowserMetrics.ts | 3 +- src/BrowserStorageProvider.ts | 4 +- src/MetricsProvider.ts | 14 +- src/NodeMetrics.ts | 3 +- src/StorageProvider.ts | 7 +- src/types/countlyNode.d.ts | 2 +- src/types/index.d.ts | 4 + test/node.spec.ts | 1 + test/node/NodeMetricsProvider.spec.ts | 113 +++++ test/testUtils.ts | 15 + 14 files changed, 752 insertions(+), 79 deletions(-) create mode 100644 test/node.spec.ts create mode 100644 test/node/NodeMetricsProvider.spec.ts create mode 100644 test/testUtils.ts diff --git a/.aegir.js b/.aegir.js index 55fe0b1..d911804 100644 --- a/.aegir.js +++ b/.aegir.js @@ -42,6 +42,6 @@ export default { bundlesizeMax: '44KB', }, test: { - build: true, + build: false, } } diff --git a/.eslintrc.json b/.eslintrc.json index 88a6764..06e917d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,5 +10,13 @@ "sourceType": "module" }, "rules": { - } + }, + "overrides": [ + { + "files": "test/**/*.spec.ts", + "rules": { + "@typescript-eslint/no-unused-expressions": "off" + } + } + ] } diff --git a/package-lock.json b/package-lock.json index 4692b51..32912a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@storybook/testing-library": "^0.0.13", "@types/mocha": "^10.0.1", "@types/react-dom": "^18.0.10", + "@types/sinon-chai": "^3.2.9", "@types/styled-components": "^5.1.26", "aegir": "^38.1.0", "babel-loader": "^9.1.2", @@ -42,8 +43,11 @@ "eslint-plugin-react": "^7.32.0", "eslint-plugin-react-hooks": "^4.6.0", "html-loader": "^4.2.0", + "npm-run-all": "^4.1.5", "postcss": "^8.4.21", "prettier": "^2.8.2", + "sinon": "^15.0.1", + "sinon-chai": "^3.7.0", "style-loader": "^3.3.1", "typescript": "^4.9.4" }, @@ -3983,6 +3987,41 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, "node_modules/@storybook/addon-actions": { "version": "6.5.15", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.15.tgz", @@ -11485,6 +11524,16 @@ "@types/sinonjs__fake-timers": "*" } }, + "node_modules/@types/sinon-chai": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", + "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "dev": true, + "dependencies": { + "@types/chai": "*", + "@types/sinon": "*" + } + }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", @@ -23904,6 +23953,12 @@ "node": ">=8" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", @@ -24471,6 +24526,12 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -25371,6 +25432,15 @@ "readable-stream": "^2.0.1" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -26898,6 +26968,34 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -27576,6 +27674,151 @@ "node": ">=10" } }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -31506,16 +31749,16 @@ } }, "node_modules/patch-package": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.0.tgz", - "integrity": "sha512-tC3EqJmo74yKqfsMzELaFwxOAu6FH6t+FzFOsnWAuARm7/n2xB5AOeOueE221eM9gtMuIKMKpF9tBy/X2mNP0Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", + "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", "dev": true, "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "cross-spawn": "^6.0.5", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^7.0.1", + "fs-extra": "^9.0.0", "is-ci": "^2.0.0", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", @@ -31599,20 +31842,6 @@ "node": ">=4.8" } }, - "node_modules/patch-package/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/patch-package/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -31622,15 +31851,6 @@ "node": ">=8" } }, - "node_modules/patch-package/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/patch-package/node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -31715,15 +31935,6 @@ "node": ">=0.6.0" } }, - "node_modules/patch-package/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/patch-package/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -31859,6 +32070,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -36012,6 +36235,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shiki": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", @@ -36069,6 +36301,55 @@ "node": ">=4" } }, + "node_modules/sinon": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", + "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "10.0.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.0.0", + "nise": "^5.1.2", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon-chai": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", + "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", + "dev": true, + "peerDependencies": { + "chai": "^4.0.0", + "sinon": ">=4.0.0" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sirv": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", @@ -42913,6 +43194,41 @@ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0" + } + }, + "@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, "@storybook/addon-actions": { "version": "6.5.15", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.15.tgz", @@ -48674,6 +48990,16 @@ "@types/sinonjs__fake-timers": "*" } }, + "@types/sinon-chai": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", + "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "dev": true, + "requires": { + "@types/chai": "*", + "@types/sinon": "*" + } + }, "@types/sinonjs__fake-timers": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", @@ -58028,6 +58354,12 @@ "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", "dev": true }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "keyv": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", @@ -58398,6 +58730,12 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -59085,6 +59423,12 @@ "readable-stream": "^2.0.1" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true + }, "meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -60162,6 +60506,36 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -62353,6 +62727,118 @@ } } }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -63463,16 +63949,16 @@ } }, "patch-package": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.0.tgz", - "integrity": "sha512-tC3EqJmo74yKqfsMzELaFwxOAu6FH6t+FzFOsnWAuARm7/n2xB5AOeOueE221eM9gtMuIKMKpF9tBy/X2mNP0Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", + "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", "dev": true, "requires": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "cross-spawn": "^6.0.5", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^7.0.1", + "fs-extra": "^9.0.0", "is-ci": "^2.0.0", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", @@ -63531,32 +64017,12 @@ "which": "^1.2.9" } }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -63617,12 +64083,6 @@ "os-tmpdir": "~1.0.2" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -63733,6 +64193,12 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -66861,6 +67327,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true + }, "shiki": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", @@ -66911,6 +67383,44 @@ } } }, + "sinon": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", + "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "10.0.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.0.0", + "nise": "^5.1.2", + "supports-color": "^7.2.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sinon-chai": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", + "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", + "dev": true, + "requires": {} + }, "sirv": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", diff --git a/package.json b/package.json index 9611b14..e86945e 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,12 @@ "lint": "aegir lint", "release": "aegir release", "build": "aegir build", - "test": "aegir test", + "pretest": "run-s build", + "test": "run-s test:*", + "test:node": "aegir test -t node -f 'dist/test/node.spec.js'", + "test:browser": "aegir test -t browser -f 'dist/test/browser.spec.js'", + "test:webworker": "aegir test -t webworker -f 'dist/test/webworker.spec.js'", + "test:electron-main": "aegir test -t electron-main -f 'dist/test/eletron-main.spec.js'", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook" }, @@ -175,6 +180,7 @@ "@storybook/testing-library": "^0.0.13", "@types/mocha": "^10.0.1", "@types/react-dom": "^18.0.10", + "@types/sinon-chai": "^3.2.9", "@types/styled-components": "^5.1.26", "aegir": "^38.1.0", "babel-loader": "^9.1.2", @@ -186,8 +192,11 @@ "eslint-plugin-react": "^7.32.0", "eslint-plugin-react-hooks": "^4.6.0", "html-loader": "^4.2.0", + "npm-run-all": "^4.1.5", "postcss": "^8.4.21", "prettier": "^2.8.2", + "sinon": "^15.0.1", + "sinon-chai": "^3.7.0", "style-loader": "^3.3.1", "typescript": "^4.9.4" }, diff --git a/src/BrowserMetrics.ts b/src/BrowserMetrics.ts index c766474..40cd558 100644 --- a/src/BrowserMetrics.ts +++ b/src/BrowserMetrics.ts @@ -2,9 +2,10 @@ import Countly from 'countly-sdk-web' import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsProvider.js' import { BrowserStorageProvider } from './BrowserStorageProvider.js' +import type { MetricProviderOptionalConstructorArgs, WithOptional } from './types/index.js' export class BrowserMetricsProvider extends MetricsProvider { - constructor (args: Omit, 'metricsService'> & Partial, 'metricsService'>>) { + constructor (args: WithOptional, MetricProviderOptionalConstructorArgs>) { super({ metricsService: Countly, storageProvider: new BrowserStorageProvider(), diff --git a/src/BrowserStorageProvider.ts b/src/BrowserStorageProvider.ts index e13bd4d..1fd68ee 100644 --- a/src/BrowserStorageProvider.ts +++ b/src/BrowserStorageProvider.ts @@ -2,7 +2,7 @@ import type { consentTypes } from './types/index.js' import type { StorageProvider } from './StorageProvider.js' export class BrowserStorageProvider implements StorageProvider { - setStore(consentArray: consentTypes[]) { + setStore (consentArray: consentTypes[]): void { try { const jsonString = JSON.stringify(consentArray) globalThis.localStorage.setItem('@ipfs-shipyard/ignite-metrics:consent', jsonString) @@ -12,7 +12,7 @@ export class BrowserStorageProvider implements StorageProvider { } } - getStore(): consentTypes[] { + getStore (): consentTypes[] { try { const jsonString = globalThis.localStorage.getItem('@ipfs-shipyard/ignite-metrics:consent') if (jsonString != null) { diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index 6b4febb..1d18d20 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -30,6 +30,7 @@ export default class MetricsProvider { private readonly _consentGranted: Set = new Set() private readonly metricsService: T private readonly storageProvider: StorageProvider | null + private readonly initDone: boolean = false constructor (config: MetricsProviderConstructorOptions) { const serviceConfig = { @@ -47,7 +48,11 @@ export default class MetricsProvider { this.setupAutoTrack() } - this.getConsentStore().forEach((consent) => { this.addConsent(consent) }) + const existingConsent = this.getConsentStore() + if (existingConsent.length > 0) { + this.addConsent(existingConsent) + } + this.initDone = true } mapAllEvents (eventMap: Record): Record { @@ -92,7 +97,12 @@ export default class MetricsProvider { } private setConsentStore (): void { - if (this.storageProvider != null) { + /** + * Only set the consent store if + * 1. we have a storage provider + * 2. we're out of the initialization phase. + */ + if (this.storageProvider != null && this.initDone) { this.storageProvider.setStore(Array.from(this._consentGranted)) } } diff --git a/src/NodeMetrics.ts b/src/NodeMetrics.ts index d7de2e0..09634b0 100644 --- a/src/NodeMetrics.ts +++ b/src/NodeMetrics.ts @@ -1,9 +1,10 @@ import Countly from 'countly-sdk-nodejs' import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsProvider.js' +import type { MetricProviderOptionalConstructorArgs, WithOptional } from './types/index.js' export class NodeMetricsProvider extends MetricsProvider { - constructor (args: Omit, 'metricsService'>) { + constructor (args: WithOptional, MetricProviderOptionalConstructorArgs>) { super({ metricsService: Countly, ...args diff --git a/src/StorageProvider.ts b/src/StorageProvider.ts index 53654e0..6132d51 100644 --- a/src/StorageProvider.ts +++ b/src/StorageProvider.ts @@ -6,15 +6,16 @@ export interface StorageProviderInterface { } export class StorageProvider { - constructor(options: StorageProviderInterface) { + constructor (options: StorageProviderInterface) { this.setStore = options.setStore ?? this.setStore this.getStore = options.getStore ?? this.setStore } - setStore(values: consentTypes[]): void { + setStore (values: consentTypes[]): void { throw new Error('Method not implemented') } - getStore(): consentTypes[] { + + getStore (): consentTypes[] { throw new Error('Method not implemented') } } diff --git a/src/types/countlyNode.d.ts b/src/types/countlyNode.d.ts index 9350739..130a6ee 100644 --- a/src/types/countlyNode.d.ts +++ b/src/types/countlyNode.d.ts @@ -1,5 +1,5 @@ declare module 'countly-sdk-nodejs' { - type missingNodeSdkMethods = 'track_clicks' | 'track_forms' | 'track_links' | 'track_scrolls' | 'track_sessions' + type missingNodeSdkMethods = 'track_clicks' | 'track_domains' | 'track_forms' | 'track_links' | 'track_scrolls' | 'track_sessions' export type CountlyNodeSdk = Omit const Countly: CountlyNodeSdk export type metricFeatures = import('countly-sdk-web').metricFeatures diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 99beef8..1f64039 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,3 +1,7 @@ export type consentTypesExceptAll = 'minimal' | 'performance' | 'ux' | 'feedback' | 'location' export type consentTypes = 'all' | consentTypesExceptAll + +export type WithOptional = Pick, K> & Omit + +export type MetricProviderOptionalConstructorArgs = 'metricsService' | 'storageProvider' diff --git a/test/node.spec.ts b/test/node.spec.ts new file mode 100644 index 0000000..41755c6 --- /dev/null +++ b/test/node.spec.ts @@ -0,0 +1 @@ +import './node/NodeMetricsProvider.spec.js' diff --git a/test/node/NodeMetricsProvider.spec.ts b/test/node/NodeMetricsProvider.spec.ts new file mode 100644 index 0000000..cfa5e3c --- /dev/null +++ b/test/node/NodeMetricsProvider.spec.ts @@ -0,0 +1,113 @@ +import CountlyNodeSdk from 'countly-sdk-nodejs' + +import { expect, sinon } from '../testUtils.js' +import { NodeMetricsProvider } from '../../src/NodeMetrics.js' +import type { StorageProvider } from '../../src/StorageProvider.js' +import type { consentTypes } from '../../src/types/index.js' + +const sandbox = sinon.createSandbox() + +describe('NodeMetricsProvider', function () { + let countlyStub: sinon.SinonStubbedInstance + let storageProviderStub: sinon.SinonStubbedInstance> + beforeEach(function () { + countlyStub = sandbox.stub(CountlyNodeSdk) + + storageProviderStub = sandbox.stub({ setStore: (args: consentTypes[]) => {}, getStore: (): consentTypes[] => ['minimal'] }) + }) + afterEach(function () { + sandbox.restore() + }) + describe('instantiation', function () { + it('with defaults', function () { + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar' }) + expect(telemetry).to.be.an.instanceOf(NodeMetricsProvider) + expect(telemetry).to.have.property('metricsService') + expect(countlyStub.init).to.have.callCount(1) + expect(countlyStub.group_features).to.have.callCount(1) + expect(telemetry).to.have.property('storageProvider').that.is.null + + // countly methods are not called + expect(countlyStub.add_consent).not.to.have.been.called + expect(countlyStub.remove_consent).not.to.have.been.called + + // autoTrack methods are called + expect(countlyStub.track_errors).to.have.callCount(1) + expect(countlyStub.track_pageview).to.have.callCount(1) + expect(countlyStub.track_view).to.have.callCount(1) + }) + + it('with no autotrack', function () { + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', autoTrack: false }) + expect(telemetry).to.be.an.instanceOf(NodeMetricsProvider) + expect(telemetry).to.have.property('metricsService') + expect(countlyStub.init).to.have.callCount(1) + expect(countlyStub.group_features).to.have.callCount(1) + expect(telemetry).to.have.property('storageProvider').that.is.null + + // countly methods are not called + expect(countlyStub.add_consent).not.to.have.been.called + expect(countlyStub.remove_consent).not.to.have.been.called + + // autoTrack methods are not called + expect(countlyStub.track_errors).not.to.have.been.called + expect(countlyStub.track_pageview).not.to.have.been.called + expect(countlyStub.track_view).not.to.have.been.called + }) + }) + + describe('StorageProvider usecases', function () { + it('User has no consent stored', function () { + storageProviderStub.getStore.returns([]) + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) + expect(telemetry).to.have.property('storageProvider').that.is.not.null + + // storageProvider methods are not called when loading. + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).not.to.have.been.called + expect(storageProviderStub.setStore).not.to.have.been.called + }) + + it('User has single consent stored', function () { + storageProviderStub.getStore.returns(['minimal']) + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) + expect(telemetry).to.have.property('storageProvider').that.is.not.null + + // storageProvider methods are called when loading. + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).to.have.been.calledWith(['minimal']) + expect(storageProviderStub.setStore).not.to.have.been.called + }) + + it('User updates consent', function () { + const storedConsent: consentTypes[] = ['minimal'] + storageProviderStub.getStore.returns(storedConsent) + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) + expect(telemetry).to.have.property('storageProvider').that.is.not.null + + // storageProvider methods are not called when loading. + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) + expect(countlyStub.add_consent).to.have.callCount(1) + expect(storageProviderStub.setStore).not.to.have.been.called + telemetry.addConsent('performance') + expect(countlyStub.add_consent).to.have.callCount(2) + expect(countlyStub.add_consent.secondCall.args[0]).to.deep.equal(['performance']) + expect(storageProviderStub.setStore.firstCall.args[0]).to.deep.equal(['minimal', 'performance']) + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(storageProviderStub.setStore).to.have.callCount(1) + }) + + it('User has multiple consents stored', function () { + const storedConsent: consentTypes[] = ['minimal', 'performance'] + storageProviderStub.getStore.returns(storedConsent) + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) + expect(telemetry).to.have.property('storageProvider').that.is.not.null + + // storageProvider methods are not called when loading. + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) + expect(storageProviderStub.setStore).not.to.have.been.called + }) + }) +}) diff --git a/test/testUtils.ts b/test/testUtils.ts new file mode 100644 index 0000000..ff51c77 --- /dev/null +++ b/test/testUtils.ts @@ -0,0 +1,15 @@ +import chai from 'chai' +import sinonChai from 'sinon-chai' +import sinon from 'sinon' + +chai.use(sinonChai) +const expect = chai.expect + +export type countlyMethodStub = sinon.SinonStub<[configOptions?: T], R> | sinon.SinonStub<[arg0: T], R> + +export { + expect, + chai, + sinonChai, + sinon +} From f01a86b771d575117f439a5d3b98b4c6ca1f86e9 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 22:44:05 -0800 Subject: [PATCH 13/16] fix: code cleanup and issues with serviceConfig --- .aegir.js | 4 ++++ package.json | 12 +++++------ ...erMetrics.ts => BrowserMetricsProvider.ts} | 1 - src/MetricsProvider.ts | 7 ++++--- ...{NodeMetrics.ts => NodeMetricsProvider.ts} | 0 src/config.ts | 3 ++- src/index.ts | 4 ++-- test/node/NodeMetricsProvider.spec.ts | 21 ++++++++++++++++++- tsconfig.json | 1 + 9 files changed, 39 insertions(+), 14 deletions(-) rename src/{BrowserMetrics.ts => BrowserMetricsProvider.ts} (99%) rename src/{NodeMetrics.ts => NodeMetricsProvider.ts} (100%) diff --git a/.aegir.js b/.aegir.js index d911804..c0b941e 100644 --- a/.aegir.js +++ b/.aegir.js @@ -12,6 +12,10 @@ export default { types: true, bundle: false, config: { + define: { + window: 'globalThis', + global: 'globalThis', + }, format: 'esm', external: ['electron', '#ansi-styles', 'yargs/yargs', '#supports-color'], plugins: [ diff --git a/package.json b/package.json index e86945e..3e24cc8 100644 --- a/package.json +++ b/package.json @@ -35,18 +35,18 @@ "import": "./dist/index.min.js" }, "./vanilla": { - "browser": "./dist/src/BrowserMetrics.js", - "import": "./dist/src/NodeMetrics.js" + "browser": "./dist/src/BrowserMetricsProvider.js", + "import": "./dist/src/NodeMetricsProvider.js" }, "./browser-vanilla": { "types": "./dist/src/BrowserMetrics.d.ts", - "browser": "./dist/src/BrowserMetrics.js", - "import": "./dist/src/BrowserMetrics.js" + "browser": "./dist/src/BrowserMetricsProvider.js", + "import": "./dist/src/BrowserMetricsProvider.js" }, "./node-vanilla": { "types": "./dist/src/NodeMetrics.d.ts", - "node": "./dist/src/NodeMetrics.js", - "import": "./dist/src/NodeMetrics.js" + "node": "./dist/src/NodeMetricsProvider.js", + "import": "./dist/src/NodeMetricsProvider.js" }, "./*": { "types": "./dist/src/*.d.ts", diff --git a/src/BrowserMetrics.ts b/src/BrowserMetricsProvider.ts similarity index 99% rename from src/BrowserMetrics.ts rename to src/BrowserMetricsProvider.ts index 40cd558..d8368a6 100644 --- a/src/BrowserMetrics.ts +++ b/src/BrowserMetricsProvider.ts @@ -1,5 +1,4 @@ import Countly from 'countly-sdk-web' - import MetricsProvider, { MetricsProviderConstructorOptions } from './MetricsProvider.js' import { BrowserStorageProvider } from './BrowserStorageProvider.js' import type { MetricProviderOptionalConstructorArgs, WithOptional } from './types/index.js' diff --git a/src/MetricsProvider.ts b/src/MetricsProvider.ts index 1d18d20..8536463 100644 --- a/src/MetricsProvider.ts +++ b/src/MetricsProvider.ts @@ -35,7 +35,8 @@ export default class MetricsProvider { constructor (config: MetricsProviderConstructorOptions) { const serviceConfig = { ...COUNTLY_SETUP_DEFAULTS, - ...config + ...config, + app_key: config.appKey } const { autoTrack, metricsService, storageProvider } = serviceConfig this.metricsService = metricsService @@ -69,12 +70,12 @@ export default class MetricsProvider { setupAutoTrack (): void { const webSdk = this.metricsService as CountlyWebSdk webSdk.track_clicks?.() - this.metricsService.track_errors() webSdk.track_forms?.() webSdk.track_links?.() - this.metricsService.track_pageview() webSdk.track_scrolls?.() webSdk.track_sessions?.() + this.metricsService.track_errors() + this.metricsService.track_pageview() this.metricsService.track_view() } diff --git a/src/NodeMetrics.ts b/src/NodeMetricsProvider.ts similarity index 100% rename from src/NodeMetrics.ts rename to src/NodeMetricsProvider.ts diff --git a/src/config.ts b/src/config.ts index 85b4fcc..8f93ff1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,5 +5,6 @@ export const COUNTLY_SETUP_DEFAULTS = { interval: 5000, max_events: 500, queue_size: 1000, - session_update: 60 + session_update: 60, + require_consent: true } diff --git a/src/index.ts b/src/index.ts index 43115db..b2d6921 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ export * from './components/index.js' export * from './config.js' -export * from './BrowserMetrics.js' +export * from './BrowserMetricsProvider.js' export * from './MetricsProvider.js' -export * from './NodeMetrics.js' +export * from './NodeMetricsProvider.js' export * from './types/index.js' diff --git a/test/node/NodeMetricsProvider.spec.ts b/test/node/NodeMetricsProvider.spec.ts index cfa5e3c..1b11417 100644 --- a/test/node/NodeMetricsProvider.spec.ts +++ b/test/node/NodeMetricsProvider.spec.ts @@ -1,7 +1,7 @@ import CountlyNodeSdk from 'countly-sdk-nodejs' import { expect, sinon } from '../testUtils.js' -import { NodeMetricsProvider } from '../../src/NodeMetrics.js' +import { NodeMetricsProvider } from '../../src/NodeMetricsProvider.js' import type { StorageProvider } from '../../src/StorageProvider.js' import type { consentTypes } from '../../src/types/index.js' @@ -75,6 +75,7 @@ describe('NodeMetricsProvider', function () { // storageProvider methods are called when loading. expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).to.have.callCount(1) expect(countlyStub.add_consent).to.have.been.calledWith(['minimal']) expect(storageProviderStub.setStore).not.to.have.been.called }) @@ -109,5 +110,23 @@ describe('NodeMetricsProvider', function () { expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) expect(storageProviderStub.setStore).not.to.have.been.called }) + + it('User removes consent', function () { + const storedConsent: consentTypes[] = ['minimal'] + storageProviderStub.getStore.returns(storedConsent) + const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) + expect(telemetry).to.have.property('storageProvider').that.is.not.null + + // storageProvider methods are not called when loading. + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) + expect(storageProviderStub.setStore).not.to.have.been.called + telemetry.removeConsent('minimal') + expect(countlyStub.remove_consent).to.have.callCount(1) + expect(countlyStub.remove_consent.firstCall.args[0]).to.deep.equal(['minimal']) + expect(storageProviderStub.setStore.firstCall.args[0]).to.deep.equal([]) + expect(storageProviderStub.getStore).to.have.callCount(1) + expect(storageProviderStub.setStore).to.have.callCount(1) + }) }) }) diff --git a/tsconfig.json b/tsconfig.json index 61b666f..8dfc801 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "target": "ES6", "allowSyntheticDefaultImports": true, "moduleResolution": "NodeNext", + "module": "es2022", "typeRoots": [ "src/types" ], From 77a95c5da67fc751150ed89b79392044443d291e Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:29:00 -0800 Subject: [PATCH 14/16] fix: tests cleanup --- test/node/NodeMetricsProvider.spec.ts | 87 ++++++++++++--------------- test/testUtils.ts | 13 ++++ 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/test/node/NodeMetricsProvider.spec.ts b/test/node/NodeMetricsProvider.spec.ts index 1b11417..4d31d99 100644 --- a/test/node/NodeMetricsProvider.spec.ts +++ b/test/node/NodeMetricsProvider.spec.ts @@ -1,6 +1,6 @@ import CountlyNodeSdk from 'countly-sdk-nodejs' -import { expect, sinon } from '../testUtils.js' +import { ensureCall, expect, sinon } from '../testUtils.js' import { NodeMetricsProvider } from '../../src/NodeMetricsProvider.js' import type { StorageProvider } from '../../src/StorageProvider.js' import type { consentTypes } from '../../src/types/index.js' @@ -23,36 +23,36 @@ describe('NodeMetricsProvider', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar' }) expect(telemetry).to.be.an.instanceOf(NodeMetricsProvider) expect(telemetry).to.have.property('metricsService') - expect(countlyStub.init).to.have.callCount(1) - expect(countlyStub.group_features).to.have.callCount(1) + ensureCall({ spy: countlyStub.init, callCount: 1 }) + ensureCall({ spy: countlyStub.group_features, callCount: 1 }) expect(telemetry).to.have.property('storageProvider').that.is.null // countly methods are not called - expect(countlyStub.add_consent).not.to.have.been.called - expect(countlyStub.remove_consent).not.to.have.been.called + ensureCall({ spy: countlyStub.add_consent, callCount: 0 }) + ensureCall({ spy: countlyStub.remove_consent, callCount: 0 }) // autoTrack methods are called - expect(countlyStub.track_errors).to.have.callCount(1) - expect(countlyStub.track_pageview).to.have.callCount(1) - expect(countlyStub.track_view).to.have.callCount(1) + ensureCall({ spy: countlyStub.track_errors, callCount: 1 }) + ensureCall({ spy: countlyStub.track_pageview, callCount: 1 }) + ensureCall({ spy: countlyStub.track_view, callCount: 1 }) }) it('with no autotrack', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', autoTrack: false }) expect(telemetry).to.be.an.instanceOf(NodeMetricsProvider) expect(telemetry).to.have.property('metricsService') - expect(countlyStub.init).to.have.callCount(1) - expect(countlyStub.group_features).to.have.callCount(1) + ensureCall({ spy: countlyStub.init, callCount: 1 }) + ensureCall({ spy: countlyStub.group_features, callCount: 1 }) expect(telemetry).to.have.property('storageProvider').that.is.null // countly methods are not called - expect(countlyStub.add_consent).not.to.have.been.called - expect(countlyStub.remove_consent).not.to.have.been.called + ensureCall({ spy: countlyStub.add_consent, callCount: 0 }) + ensureCall({ spy: countlyStub.remove_consent, callCount: 0 }) // autoTrack methods are not called - expect(countlyStub.track_errors).not.to.have.been.called - expect(countlyStub.track_pageview).not.to.have.been.called - expect(countlyStub.track_view).not.to.have.been.called + ensureCall({ spy: countlyStub.track_errors, callCount: 0 }) + ensureCall({ spy: countlyStub.track_pageview, callCount: 0 }) + ensureCall({ spy: countlyStub.track_view, callCount: 0 }) }) }) @@ -62,22 +62,22 @@ describe('NodeMetricsProvider', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) expect(telemetry).to.have.property('storageProvider').that.is.not.null - // storageProvider methods are not called when loading. - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(countlyStub.add_consent).not.to.have.been.called - expect(storageProviderStub.setStore).not.to.have.been.called + // only storageProvider.getStore called when loading. + ensureCall({ spy: countlyStub.add_consent, callCount: 0 }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 0 }) + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) }) it('User has single consent stored', function () { - storageProviderStub.getStore.returns(['minimal']) + const storedConsent: consentTypes[] = ['minimal'] + storageProviderStub.getStore.returns(storedConsent) const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) expect(telemetry).to.have.property('storageProvider').that.is.not.null // storageProvider methods are called when loading. - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(countlyStub.add_consent).to.have.callCount(1) - expect(countlyStub.add_consent).to.have.been.calledWith(['minimal']) - expect(storageProviderStub.setStore).not.to.have.been.called + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) + ensureCall({ spy: countlyStub.add_consent, callCount: 1, callIndex: 0, expectedArgs: storedConsent }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 0 }) }) it('User updates consent', function () { @@ -86,17 +86,13 @@ describe('NodeMetricsProvider', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) expect(telemetry).to.have.property('storageProvider').that.is.not.null - // storageProvider methods are not called when loading. - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) - expect(countlyStub.add_consent).to.have.callCount(1) - expect(storageProviderStub.setStore).not.to.have.been.called + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) + ensureCall({ spy: countlyStub.add_consent, callCount: 1, callIndex: 0, expectedArgs: storedConsent }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 0 }) telemetry.addConsent('performance') - expect(countlyStub.add_consent).to.have.callCount(2) - expect(countlyStub.add_consent.secondCall.args[0]).to.deep.equal(['performance']) - expect(storageProviderStub.setStore.firstCall.args[0]).to.deep.equal(['minimal', 'performance']) - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(storageProviderStub.setStore).to.have.callCount(1) + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) + ensureCall({ spy: countlyStub.add_consent, callCount: 2, callIndex: 1, expectedArgs: ['performance'] }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 1, callIndex: 0, expectedArgs: ['minimal', 'performance'] }) }) it('User has multiple consents stored', function () { @@ -105,10 +101,9 @@ describe('NodeMetricsProvider', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) expect(telemetry).to.have.property('storageProvider').that.is.not.null - // storageProvider methods are not called when loading. - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) - expect(storageProviderStub.setStore).not.to.have.been.called + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) + ensureCall({ spy: countlyStub.add_consent, callCount: 1, callIndex: 0, expectedArgs: storedConsent }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 0 }) }) it('User removes consent', function () { @@ -117,16 +112,14 @@ describe('NodeMetricsProvider', function () { const telemetry = new NodeMetricsProvider({ appKey: 'foo', url: 'bar', storageProvider: storageProviderStub }) expect(telemetry).to.have.property('storageProvider').that.is.not.null - // storageProvider methods are not called when loading. - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(countlyStub.add_consent).to.have.been.calledWith(storedConsent) - expect(storageProviderStub.setStore).not.to.have.been.called + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) + ensureCall({ spy: countlyStub.add_consent, callCount: 1, callIndex: 0, expectedArgs: storedConsent }) + ensureCall({ spy: countlyStub.remove_consent, callCount: 0 }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 0 }) telemetry.removeConsent('minimal') - expect(countlyStub.remove_consent).to.have.callCount(1) - expect(countlyStub.remove_consent.firstCall.args[0]).to.deep.equal(['minimal']) - expect(storageProviderStub.setStore.firstCall.args[0]).to.deep.equal([]) - expect(storageProviderStub.getStore).to.have.callCount(1) - expect(storageProviderStub.setStore).to.have.callCount(1) + ensureCall({ spy: storageProviderStub.getStore, callCount: 1 }) // no change + ensureCall({ spy: countlyStub.remove_consent, callCount: 1, callIndex: 0, expectedArgs: ['minimal'] }) + ensureCall({ spy: storageProviderStub.setStore, callCount: 1, callIndex: 0, expectedArgs: [] }) }) }) }) diff --git a/test/testUtils.ts b/test/testUtils.ts index ff51c77..d362d63 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -6,7 +6,20 @@ chai.use(sinonChai) const expect = chai.expect export type countlyMethodStub = sinon.SinonStub<[configOptions?: T], R> | sinon.SinonStub<[arg0: T], R> +export interface EnsureCallOptions { + spy: sinon.SinonStub + callCount: number + callIndex?: number + expectedArgs?: T[number] + argsIndex?: number +} +export const ensureCall = (config: EnsureCallOptions): void => { + expect(config.spy).to.have.callCount(config.callCount) + if (config.expectedArgs != null && config.callIndex != null) { + expect(config.spy.getCall(config.callIndex).args[config.argsIndex ?? 0]).to.deep.equal(config.expectedArgs) + } +} export { expect, chai, From af2b67df49f6b494eb50f8ab0961eb6ab0f79774 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:33:18 -0800 Subject: [PATCH 15/16] chore: remove unused test scripts --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 3e24cc8..dafa2ff 100644 --- a/package.json +++ b/package.json @@ -151,9 +151,6 @@ "pretest": "run-s build", "test": "run-s test:*", "test:node": "aegir test -t node -f 'dist/test/node.spec.js'", - "test:browser": "aegir test -t browser -f 'dist/test/browser.spec.js'", - "test:webworker": "aegir test -t webworker -f 'dist/test/webworker.spec.js'", - "test:electron-main": "aegir test -t electron-main -f 'dist/test/eletron-main.spec.js'", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook" }, From ef9d4cc28add878ad50bac2a6f636b1810a531e2 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 20 Jan 2023 00:05:18 -0800 Subject: [PATCH 16/16] fix: remove dist/test from npm package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index dafa2ff..a21a247 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "files": [ "src", "dist", + "!dist/test", "!**/*.tsbuildinfo" ], "exports": {