diff --git a/apps/delivery-options/private/getSharedConfig.ts b/apps/delivery-options/private/getSharedConfig.ts index 15d56759..b85703b7 100644 --- a/apps/delivery-options/private/getSharedConfig.ts +++ b/apps/delivery-options/private/getSharedConfig.ts @@ -1,12 +1,14 @@ import {fileURLToPath} from 'node:url'; import {type UserConfig, type ConfigEnv} from 'vite'; import {resolveAlias} from '@myparcel-do/build-vite'; +import {version} from '../package.json'; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export const getSharedConfig = ({mode}: ConfigEnv) => { return { define: { 'process.env.NODE_ENV': JSON.stringify(mode), + __VERSION__: JSON.stringify(version), __CLASS_BASE__: JSON.stringify('myparcel-delivery-options'), __URL_DOCUMENTATION__: JSON.stringify('https://developer.myparcel.nl/documentation/60.delivery-options.html'), __URL_SANDBOX__: JSON.stringify('https://myparcelnl.github.io/delivery-options/'), diff --git a/apps/delivery-options/src/env.d.ts b/apps/delivery-options/src/env.d.ts index 52e443b5..58b504a3 100644 --- a/apps/delivery-options/src/env.d.ts +++ b/apps/delivery-options/src/env.d.ts @@ -10,5 +10,11 @@ declare module '*.vue' { declare const __CLASS_BASE__: string; +declare const __VERSION__: string; + +declare const __URL_DOCUMENTATION__: string; + +declare const __URL_SANDBOX__: string; + // Declared here to avoid "TS2686: L refers to a UMD global, but the current file is a module. Consider adding an import instead." error. declare const L; diff --git a/apps/sandbox/vite.config.ts b/apps/sandbox/vite.config.ts index f0f074c1..b0e2787c 100644 --- a/apps/sandbox/vite.config.ts +++ b/apps/sandbox/vite.config.ts @@ -3,6 +3,7 @@ import customTsConfig from 'vite-plugin-custom-tsconfig'; import {isCI} from 'ci-info'; import vue from '@vitejs/plugin-vue'; import {resolveAlias} from '@myparcel-do/build-vite'; +import {version} from './package.json'; export const PORT = 9860; @@ -39,6 +40,8 @@ export default defineConfig(({mode}) => { define: { 'process.env.NODE_ENV': JSON.stringify(mode), + __VERSION__: JSON.stringify(version), + __CLASS_BASE__: JSON.stringify('myparcel-delivery-options'), }, test: { diff --git a/libs/shared/src/__tests__/useMockSdk.ts b/libs/shared/src/__tests__/useMockSdk.ts new file mode 100644 index 00000000..26976013 --- /dev/null +++ b/libs/shared/src/__tests__/useMockSdk.ts @@ -0,0 +1,71 @@ +import {ref, type Ref} from 'vue'; +import { + type ClientConfig, + type AbstractPublicEndpoint, + type Options, + GetCarrier, + GetCarriers, + GetDeliveryOptions, + GetPickupLocations, + type EndpointResponse, +} from '@myparcel/sdk'; +import {mockGetCarrier, mockGetCarriers, mockGetDeliveryOptions, mockGetPickupLocations} from './mocks'; + +export type LastOptions = AbstractPublicEndpoint> = Readonly<{ + endpoint: Readonly; + options: Readonly>; +}>; + +const history = ref([]); +const clientConfig = ref(); + +interface UseMockSdk { + clientConfig: Ref; + history: Ref; + + doRequest>(endpoint: E, options: Options): Promise>; + + reset(): void; +} + +export const useMockSdk = (): UseMockSdk => { + const doRequest = async >( + endpoint: E, + options: Options, + ): Promise> => { + history.value.push({ + endpoint: Object.freeze(endpoint), + options: Object.freeze(options), + }); + + if (endpoint instanceof GetCarrier) { + return Promise.resolve(mockGetCarrier(endpoint, options)); + } + + if (endpoint instanceof GetCarriers) { + return Promise.resolve(mockGetCarriers(endpoint, options)); + } + + if (endpoint instanceof GetDeliveryOptions) { + return Promise.resolve(mockGetDeliveryOptions(endpoint, options)); + } + + if (endpoint instanceof GetPickupLocations) { + return Promise.resolve(mockGetPickupLocations(endpoint, options)); + } + + throw new Error(`Unknown request: ${endpoint.name}`); + }; + + const reset = () => { + history.value = []; + clientConfig.value = undefined; + }; + + return { + doRequest, + reset, + clientConfig, + history, + }; +}; diff --git a/libs/shared/src/__tests__/vitest-setup.ts b/libs/shared/src/__tests__/vitest-setup.ts index 0f540737..31ff88fb 100644 --- a/libs/shared/src/__tests__/vitest-setup.ts +++ b/libs/shared/src/__tests__/vitest-setup.ts @@ -1,17 +1,10 @@ /* eslint-disable */ import {afterEach, vi} from 'vitest'; -import {mockGetCarrier, mockGetCarriers, mockGetDeliveryOptions, mockGetPickupLocations} from './mocks'; -import { - type AbstractPublicEndpoint, - type EndpointResponse, - GetCarrier, - GetCarriers, - GetDeliveryOptions, - GetPickupLocations, - type Options, -} from '@myparcel/sdk'; +import type {ClientConfig} from '@myparcel/sdk'; +import {type AbstractPublicEndpoint, type EndpointResponse, type Options} from '@myparcel/sdk'; import {cleanup} from '@testing-library/vue'; import {useRequestStorage} from '../composables'; +import {useMockSdk} from './useMockSdk'; const { afterEachHooks } = vi.hoisted(() => { return {afterEachHooks: [] as (() => void)[]}; @@ -23,24 +16,18 @@ vi.mock('@myparcel/sdk', async (importOriginal) => { return { ...original, FetchClient: class FetchClient extends original.FetchClient { - public doRequest>(endpoint: E, options: Options): Promise> { - if (endpoint instanceof GetCarrier) { - return Promise.resolve(mockGetCarrier(endpoint, options)); - } + constructor(config?: ClientConfig) { + super(config); - if (endpoint instanceof GetCarriers) { - return Promise.resolve(mockGetCarriers(endpoint, options)); - } + const {clientConfig} = useMockSdk(); - if (endpoint instanceof GetDeliveryOptions) { - return Promise.resolve(mockGetDeliveryOptions(endpoint, options)); - } + clientConfig.value = config; + } - if (endpoint instanceof GetPickupLocations) { - return Promise.resolve(mockGetPickupLocations(endpoint, options)); - } + public doRequest>(endpoint: E, options: Options): Promise> { + const {doRequest} = useMockSdk(); - throw new Error(`Unknown request: ${endpoint.name}`); + return doRequest(endpoint, options); } }, }; @@ -64,6 +51,7 @@ vi.mock('@vueuse/core', async (importOriginal) => { afterEach(() => { useRequestStorage().clear(); + useMockSdk().reset(); afterEachHooks.forEach(hook => hook()); diff --git a/libs/shared/src/composables/useSdk.spec.ts b/libs/shared/src/composables/useSdk.spec.ts new file mode 100644 index 00000000..c462a90e --- /dev/null +++ b/libs/shared/src/composables/useSdk.spec.ts @@ -0,0 +1,18 @@ +import {describe, it, expect} from 'vitest'; +import {useMockSdk} from '../__tests__/useMockSdk'; +import {useSdk} from './useSdk'; + +describe('useSdk', () => { + it('adds a user agent header', () => { + const {clientConfig} = useMockSdk(); + + useSdk(); + + expect(clientConfig.value?.headers).toEqual({ + 'X-User-Agent': `MyParcelDeliveryOptions/${__VERSION__}`, + }); + + // Make sure __VERSION__ actually returns a version string + expect(clientConfig.value?.headers?.['X-User-Agent']).toMatch(/MyParcelDeliveryOptions\/\d+\.\d+\.\d+/); + }); +}); diff --git a/libs/shared/src/composables/useSdk.ts b/libs/shared/src/composables/useSdk.ts index c1d21544..ddc9798e 100644 --- a/libs/shared/src/composables/useSdk.ts +++ b/libs/shared/src/composables/useSdk.ts @@ -12,7 +12,7 @@ export const useSdk = useMemoize(() => { return createPublicSdk( new FetchClient({ headers: { - 'X-User-Agent': 'MyParcelDeliveryOptions/1.0.0', + 'X-User-Agent': `MyParcelDeliveryOptions/${__VERSION__}`, }, }), [new GetCarrier(), new GetCarriers(), new GetDeliveryOptions(), new GetPickupLocations()], diff --git a/libs/shared/vite.config.ts b/libs/shared/vite.config.ts index 7bf62b00..79a9664b 100644 --- a/libs/shared/vite.config.ts +++ b/libs/shared/vite.config.ts @@ -1,6 +1,7 @@ import dts from 'vite-plugin-dts'; import {isCI} from 'ci-info'; import {createViteConfig} from '@myparcel-do/build-vite'; +import {version} from './package.json'; const dirname = new URL('.', import.meta.url).pathname; @@ -21,6 +22,10 @@ export default createViteConfig((env) => { }, }, + define: { + __VERSION__: JSON.stringify(version), + }, + test: { setupFiles: [`${dirname}/src/__tests__/vitest-setup.ts`], },