From 26b7bb3c1d49bf580d13fc196a34128f5c87033e Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:42:31 +0000 Subject: [PATCH 01/20] feat: Create Axios and Fetch adapters --- src/adapters/axiosAdapter.ts | 19 +++++++++++++++ src/adapters/fetchAdapter.ts | 17 +++++++++++++ src/adapters/index.ts | 33 ++++++++++++++++++++++++++ src/adapters/resolveAdapterFunction.ts | 19 +++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 src/adapters/axiosAdapter.ts create mode 100644 src/adapters/fetchAdapter.ts create mode 100644 src/adapters/index.ts create mode 100644 src/adapters/resolveAdapterFunction.ts diff --git a/src/adapters/axiosAdapter.ts b/src/adapters/axiosAdapter.ts new file mode 100644 index 0000000..fb394a7 --- /dev/null +++ b/src/adapters/axiosAdapter.ts @@ -0,0 +1,19 @@ +import axios from "axios"; +import { AdapterFunction } from "."; + +const axiosAdapter: AdapterFunction = ({ url, method, data, headers }) => { + return axios + .request({ + url, + method, + data, + headers, + }) + .then(({ data, headers, status }) => ({ + data, + headers, + status, + })); +}; + +export default axiosAdapter; diff --git a/src/adapters/fetchAdapter.ts b/src/adapters/fetchAdapter.ts new file mode 100644 index 0000000..1cdb2c3 --- /dev/null +++ b/src/adapters/fetchAdapter.ts @@ -0,0 +1,17 @@ +import { AdapterFunction } from "."; + +const fetchAdapter: AdapterFunction = ({ url, method, data, headers }) => { + return fetch(url, { + method, + body: data, + headers, + }).then(({ headers, status, json }) => { + return json().then((data) => ({ + data, + headers, + status, + })); + }); +}; + +export default fetchAdapter; diff --git a/src/adapters/index.ts b/src/adapters/index.ts new file mode 100644 index 0000000..8afa462 --- /dev/null +++ b/src/adapters/index.ts @@ -0,0 +1,33 @@ +import axiosAdapter from "./axiosAdapter"; +import fetchAdapter from "./fetchAdapter"; +import resolveAdapterFunction from "./resolveAdapterFunction"; + +interface AdapterRequest { + method: "GET" | "POST" | "PUT" | "DELETE" | string; + headers?: Record; + data?: any; +} + +interface AdapterResponse { + data: T; + headers: Record; + status: number; +} + +type AdapterPromise = Promise>; + +type AdapterFunction = ( + params: AdapterRequest & { url: string } +) => AdapterPromise; + +type Adapter = "fetch" | "axios" | AdapterFunction; + +export type { + AdapterRequest, + AdapterResponse, + AdapterPromise, + AdapterFunction, + Adapter, +}; + +export { axiosAdapter, fetchAdapter, resolveAdapterFunction }; diff --git a/src/adapters/resolveAdapterFunction.ts b/src/adapters/resolveAdapterFunction.ts new file mode 100644 index 0000000..aa73b57 --- /dev/null +++ b/src/adapters/resolveAdapterFunction.ts @@ -0,0 +1,19 @@ +import { Adapter, AdapterFunction, axiosAdapter, fetchAdapter } from "."; + +const resolveAdapterFunction = (adapter?: Adapter): AdapterFunction => { + if (typeof adapter === "function") { + return adapter; + } else { + switch (adapter) { + case "fetch": { + return fetchAdapter; + } + case "axios": + default: { + return axiosAdapter; + } + } + } +}; + +export default resolveAdapterFunction; From caddfd2a6c2f335a803091dd7b883e5e4084828f Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:46:17 +0000 Subject: [PATCH 02/20] feat: Replace fixed Axios adapter with adapter option --- src/XAPI.ts | 135 +++++++++--------- src/XAPIConfig.ts | 2 + .../getXAPILaunchData/getXAPILaunchData.ts | 33 +++-- src/resources/about/getAbout/getAbout.ts | 4 +- .../activities/getActivity/getActivity.ts | 4 +- src/resources/agents/getAgent/getAgent.ts | 4 +- .../createActivityProfile.ts | 4 +- .../deleteActivityProfile.ts | 4 +- .../getActivityProfile/getActivityProfile.ts | 4 +- .../getActivityProfiles.ts | 4 +- .../setActivityProfile/setActivityProfile.ts | 4 +- .../createAgentProfile/createAgentProfile.ts | 4 +- .../deleteAgentProfile/deleteAgentProfile.ts | 4 +- .../getAgentProfile/getAgentProfile.ts | 4 +- .../getAgentProfiles/getAgentProfiles.ts | 4 +- .../setAgentProfile/setAgentProfile.ts | 4 +- .../document/state/createState/createState.ts | 4 +- .../document/state/deleteState/deleteState.ts | 4 +- .../state/deleteStates/deleteStates.ts | 4 +- .../document/state/getState/getState.ts | 4 +- .../document/state/getStates/getStates.ts | 4 +- .../document/state/setState/setState.ts | 4 +- .../getMoreStatements/getMoreStatements.ts | 4 +- .../statement/getStatement/getStatement.ts | 8 +- .../statement/getStatements/getStatements.ts | 8 +- .../getVoidedStatement/getVoidedStatement.ts | 8 +- .../statement/sendStatement/sendStatement.ts | 4 +- .../sendStatements/sendStatements.ts | 4 +- .../statement/voidStatement/voidStatement.ts | 4 +- .../voidStatements/voidStatements.ts | 4 +- 30 files changed, 147 insertions(+), 143 deletions(-) diff --git a/src/XAPI.ts b/src/XAPI.ts index f0c9b1c..c7e41a5 100644 --- a/src/XAPI.ts +++ b/src/XAPI.ts @@ -1,9 +1,3 @@ -import axios, { - AxiosRequestConfig, - AxiosPromise, - AxiosStatic, - RawAxiosRequestHeaders, -} from "axios"; import { AttachmentUsages, Resources, Verbs, Versions } from "./constants"; import { parseMultiPart } from "./internal/multiPart"; import { formatEndpoint } from "./internal/formatEndpoint"; @@ -91,7 +85,12 @@ import { SendStatementParams } from "./resources/statement/sendStatement/SendSta import { SendStatementsParams } from "./resources/statement/sendStatements/SendStatementsParams"; import { VoidStatementParams } from "./resources/statement/voidStatement/VoidStatementParams"; import { VoidStatementsParams } from "./resources/statement/voidStatements/VoidStatementsParams"; - +import { + AdapterFunction, + AdapterPromise, + AdapterRequest, + resolveAdapterFunction, +} from "./adapters"; export * from "./helpers/getTinCanLaunchData/TinCanLaunchData"; export * from "./helpers/getXAPILaunchData/XAPILaunchData"; export * from "./resources/about/About"; @@ -146,6 +145,7 @@ class XAPI { protected endpoint: string; private headers: { [key: string]: string }; + private adapter: AdapterFunction; public constructor(params: XAPIConfig) { const version: Versions = params.version || "1.0.3"; @@ -156,18 +156,15 @@ class XAPI { // No Authorization Process and Requirements - https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#no-authorization-process-and-requirements Authorization: params.auth ? params.auth : toBasicAuth("", ""), }; - } - - public getAxios(): AxiosStatic { - return axios; + this.adapter = resolveAdapterFunction(params.adapter); } protected requestResource(params: { resource: Resources; queryParams?: RequestParams; - requestConfig?: AxiosRequestConfig | undefined; + requestConfig?: AdapterRequest | undefined; requestOptions?: GetParamsBase; - }): AxiosPromise { + }): AdapterPromise { const extendedQueryParams = Object.assign({}, params.queryParams); if (params.requestOptions?.useCacheBuster) { extendedQueryParams["cachebuster"] = new Date().getTime().toString(); @@ -178,29 +175,27 @@ class XAPI { protected requestURL( url: string, - requestConfig?: AxiosRequestConfig | undefined - ): AxiosPromise { - return axios - .request({ - method: requestConfig?.method || "GET", - url: url, - headers: { - ...this.headers, - ...(requestConfig?.headers as RawAxiosRequestHeaders), - }, - data: requestConfig?.data, - }) - .then((response) => { - const contentType = response.headers["content-type"]; - if ( - !!response.data && - contentType && - contentType.indexOf("multipart/mixed") !== -1 - ) { - response.data = parseMultiPart(response.data); - } - return response; - }); + requestConfig?: AdapterRequest | undefined + ): AdapterPromise { + return this.adapter({ + url, + method: requestConfig?.method || "GET", + headers: { + ...this.headers, + ...requestConfig?.headers, + }, + data: requestConfig?.data, + }).then((response) => { + const contentType = response.headers["content-type"]; + if ( + !!response.data && + contentType && + contentType.indexOf("multipart/mixed") !== -1 + ) { + response.data = parseMultiPart(response.data); + } + return response; + }); } private generateURL(resource: Resources, params: RequestParams): string { @@ -219,65 +214,67 @@ class XAPI { } interface XAPI { - getAbout(params?: GetAboutParams): AxiosPromise; - getActivity(params: GetActivityParams): AxiosPromise; - getAgent(params: GetAgentParams): AxiosPromise; + getAbout(params?: GetAboutParams): AdapterPromise; + getActivity(params: GetActivityParams): AdapterPromise; + getAgent(params: GetAgentParams): AdapterPromise; createActivityProfile( params: CreateActivityProfileParams - ): AxiosPromise; - setActivityProfile(params: SetActivityProfileParams): AxiosPromise; + ): AdapterPromise; + setActivityProfile(params: SetActivityProfileParams): AdapterPromise; getActivityProfiles( params: GetActivityProfilesParams - ): AxiosPromise; - getActivityProfile(params: GetActivityProfileParams): AxiosPromise; + ): AdapterPromise; + getActivityProfile( + params: GetActivityProfileParams + ): AdapterPromise; deleteActivityProfile( params: DeleteActivityProfileParams - ): AxiosPromise; - createAgentProfile(params: CreateAgentProfileParams): AxiosPromise; - setAgentProfile(params: SetAgentProfileParams): AxiosPromise; - getAgentProfiles(params: GetAgentProfilesParams): AxiosPromise; - getAgentProfile(params: GetAgentProfileParams): AxiosPromise; - deleteAgentProfile(params: DeleteAgentProfileParams): AxiosPromise; - createState(params: CreateStateParams): AxiosPromise; - setState(params: SetStateParams): AxiosPromise; - getStates(params: GetStatesParams): AxiosPromise; - getState(params: GetStateParams): AxiosPromise; - deleteState(params: DeleteStateParams): AxiosPromise; - deleteStates(params: DeleteStatesParams): AxiosPromise; + ): AdapterPromise; + createAgentProfile(params: CreateAgentProfileParams): AdapterPromise; + setAgentProfile(params: SetAgentProfileParams): AdapterPromise; + getAgentProfiles(params: GetAgentProfilesParams): AdapterPromise; + getAgentProfile(params: GetAgentProfileParams): AdapterPromise; + deleteAgentProfile(params: DeleteAgentProfileParams): AdapterPromise; + createState(params: CreateStateParams): AdapterPromise; + setState(params: SetStateParams): AdapterPromise; + getStates(params: GetStatesParams): AdapterPromise; + getState(params: GetStateParams): AdapterPromise; + deleteState(params: DeleteStateParams): AdapterPromise; + deleteStates(params: DeleteStatesParams): AdapterPromise; getStatement( params: GetStatementParamsWithAttachments - ): AxiosPromise; + ): AdapterPromise; getStatement( params: GetStatementParamsWithoutAttachments - ): AxiosPromise; + ): AdapterPromise; getStatement( params: GetStatementParams - ): AxiosPromise; + ): AdapterPromise; getVoidedStatement( params: GetVoidedStatementParamsWithAttachments - ): AxiosPromise; + ): AdapterPromise; getVoidedStatement( params: GetVoidedStatementParamsWithoutAttachments - ): AxiosPromise; + ): AdapterPromise; getVoidedStatement( params: GetVoidedStatementParams - ): AxiosPromise; + ): AdapterPromise; getStatements( params: GetStatementsParamsWithAttachments - ): AxiosPromise; + ): AdapterPromise; getStatements( params: GetStatementsParamsWithoutAttachments - ): AxiosPromise; + ): AdapterPromise; getStatements( params: GetStatementsParams - ): AxiosPromise; + ): AdapterPromise; getMoreStatements( params: GetMoreStatementsParams - ): AxiosPromise; - sendStatement(params: SendStatementParams): AxiosPromise; - sendStatements(params: SendStatementsParams): AxiosPromise; - voidStatement(params: VoidStatementParams): AxiosPromise; - voidStatements(params: VoidStatementsParams): AxiosPromise; + ): AdapterPromise; + sendStatement(params: SendStatementParams): AdapterPromise; + sendStatements(params: SendStatementsParams): AdapterPromise; + voidStatement(params: VoidStatementParams): AdapterPromise; + voidStatements(params: VoidStatementsParams): AdapterPromise; } XAPI.prototype.getAbout = getAbout; diff --git a/src/XAPIConfig.ts b/src/XAPIConfig.ts index baea6be..ce7f4c8 100644 --- a/src/XAPIConfig.ts +++ b/src/XAPIConfig.ts @@ -1,7 +1,9 @@ +import { Adapter } from "./adapters"; import { Versions } from "./constants"; export interface XAPIConfig { endpoint: string; auth?: string; version?: Versions; + adapter?: Adapter; } diff --git a/src/helpers/getXAPILaunchData/getXAPILaunchData.ts b/src/helpers/getXAPILaunchData/getXAPILaunchData.ts index e1c73e1..097fda4 100644 --- a/src/helpers/getXAPILaunchData/getXAPILaunchData.ts +++ b/src/helpers/getXAPILaunchData/getXAPILaunchData.ts @@ -1,32 +1,37 @@ +import { + Adapter, + AdapterPromise, + resolveAdapterFunction, +} from "../../adapters"; import { getSearchQueryParamsAsObject } from "../getSearchQueryParamsAsObject/getSearchQueryParamsAsObject"; -import axios from "axios"; import { XAPILaunchData } from "./XAPILaunchData"; import { XAPILaunchParameters } from "./XAPILaunchParameters"; -export function getXAPILaunchData(): Promise { +export function getXAPILaunchData(params?: { + adapter?: Adapter; +}): AdapterPromise { if (typeof location === "undefined") return Promise.reject( new Error("Environment does not support location.search") ); - const params: XAPILaunchParameters = getSearchQueryParamsAsObject( + const launchParams: XAPILaunchParameters = getSearchQueryParamsAsObject( location.search ); - if (!params.xAPILaunchService) { + if (!launchParams.xAPILaunchService) { return Promise.reject( new Error("xAPILaunchService parameter not found in URL.") ); } - const launchURL: URL = new URL(params.xAPILaunchService); - launchURL.pathname += `launch/${params.xAPILaunchKey}`; - return axios - .request({ - method: "POST", - url: launchURL.toString(), - }) - .then((response) => { - return response.data; - }); + const launchURL: URL = new URL(launchParams.xAPILaunchService); + launchURL.pathname += `launch/${launchParams.xAPILaunchKey}`; + const adapter = resolveAdapterFunction(params.adapter); + return adapter({ + method: "POST", + url: launchURL.toString(), + }).then((response) => { + return response.data; + }); } diff --git a/src/resources/about/getAbout/getAbout.ts b/src/resources/about/getAbout/getAbout.ts index 7fcbd49..1b7f1f0 100644 --- a/src/resources/about/getAbout/getAbout.ts +++ b/src/resources/about/getAbout/getAbout.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { About } from "../About"; @@ -7,7 +7,7 @@ import { GetAboutParams } from "./GetAboutParams"; export function getAbout( this: XAPI, params?: GetAboutParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.ABOUT, requestOptions: { diff --git a/src/resources/activities/getActivity/getActivity.ts b/src/resources/activities/getActivity/getActivity.ts index f04bcb0..b4cda49 100644 --- a/src/resources/activities/getActivity/getActivity.ts +++ b/src/resources/activities/getActivity/getActivity.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { Activity } from "../Activity"; @@ -7,7 +7,7 @@ import { GetActivityParams } from "./GetActivityParams"; export function getActivity( this: XAPI, params: GetActivityParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.ACTIVITIES, queryParams: { diff --git a/src/resources/agents/getAgent/getAgent.ts b/src/resources/agents/getAgent/getAgent.ts index 59d5750..9bb3463 100644 --- a/src/resources/agents/getAgent/getAgent.ts +++ b/src/resources/agents/getAgent/getAgent.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { Person } from "../Person"; @@ -7,7 +7,7 @@ import { GetAgentParams } from "./GetAgentParams"; export function getAgent( this: XAPI, params: GetAgentParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.AGENTS, queryParams: { diff --git a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.ts b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.ts index 74c2b67..a48ea15 100644 --- a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.ts +++ b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { CreateActivityProfileParams } from "./CreateActivityProfileParams"; @@ -6,7 +6,7 @@ import { CreateActivityProfileParams } from "./CreateActivityProfileParams"; export function createActivityProfile( this: XAPI, params: CreateActivityProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers[params.matchHeader] = params.etag; return this.requestResource({ diff --git a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.ts b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.ts index 43a0fc5..7b41751 100644 --- a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.ts +++ b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { DeleteActivityProfileParams } from "./DeleteActivityProfileParams"; @@ -6,7 +6,7 @@ import { DeleteActivityProfileParams } from "./DeleteActivityProfileParams"; export function deleteActivityProfile( this: XAPI, params: DeleteActivityProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers["If-Match"] = params.etag; return this.requestResource({ diff --git a/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.ts b/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.ts index 52c07c1..5991bdb 100644 --- a/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.ts +++ b/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { GetActivityProfileParams } from "./GetActivityProfileParams"; @@ -7,7 +7,7 @@ import { Document } from "../../Document"; export function getActivityProfile( this: XAPI, params: GetActivityProfileParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.ACTIVITY_PROFILE, queryParams: { diff --git a/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.ts b/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.ts index ada2173..f28676f 100644 --- a/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.ts +++ b/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { GetActivityProfilesParams } from "./GetActivityProfilesParams"; @@ -6,7 +6,7 @@ import { GetActivityProfilesParams } from "./GetActivityProfilesParams"; export function getActivityProfiles( this: XAPI, params: GetActivityProfilesParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.ACTIVITY_PROFILE, queryParams: { diff --git a/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.ts b/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.ts index 7cc572c..9e9051c 100644 --- a/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.ts +++ b/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { SetActivityProfileParams } from "./SetActivityProfileParams"; @@ -6,7 +6,7 @@ import { SetActivityProfileParams } from "./SetActivityProfileParams"; export function setActivityProfile( this: XAPI, params: SetActivityProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; headers[params.matchHeader] = params.etag; if (params.contentType) headers["Content-Type"] = params.contentType; diff --git a/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.ts b/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.ts index aa2186f..ab9cb40 100644 --- a/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.ts +++ b/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { CreateAgentProfileParams } from "./CreateAgentProfileParams"; @@ -6,7 +6,7 @@ import { CreateAgentProfileParams } from "./CreateAgentProfileParams"; export function createAgentProfile( this: XAPI, params: CreateAgentProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers[params.matchHeader] = params.etag; return this.requestResource({ diff --git a/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.ts b/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.ts index d37939c..0e9383f 100644 --- a/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.ts +++ b/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { DeleteAgentProfileParams } from "./DeleteAgentProfileParams"; @@ -6,7 +6,7 @@ import { DeleteAgentProfileParams } from "./DeleteAgentProfileParams"; export function deleteAgentProfile( this: XAPI, params: DeleteAgentProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers["If-Match"] = params.etag; return this.requestResource({ diff --git a/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.ts b/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.ts index aa30886..5bc89aa 100644 --- a/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.ts +++ b/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { Document } from "../../Document"; @@ -7,7 +7,7 @@ import { GetAgentProfileParams } from "./GetAgentProfileParams"; export function getAgentProfile( this: XAPI, params: GetAgentProfileParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.AGENT_PROFILE, queryParams: { diff --git a/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.ts b/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.ts index 15b28c1..f264f01 100644 --- a/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.ts +++ b/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { GetAgentProfilesParams } from "./GetAgentProfilesParams"; @@ -6,7 +6,7 @@ import { GetAgentProfilesParams } from "./GetAgentProfilesParams"; export function getAgentProfiles( this: XAPI, params: GetAgentProfilesParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.AGENT_PROFILE, queryParams: { diff --git a/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.ts b/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.ts index 47420d1..eb8dc2a 100644 --- a/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.ts +++ b/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { SetAgentProfileParams } from "./SetAgentProfileParams"; @@ -6,7 +6,7 @@ import { SetAgentProfileParams } from "./SetAgentProfileParams"; export function setAgentProfile( this: XAPI, params: SetAgentProfileParams -): AxiosPromise { +): AdapterPromise { const headers = {}; headers[params.matchHeader] = params.etag; if (params.contentType) headers["Content-Type"] = params.contentType; diff --git a/src/resources/document/state/createState/createState.ts b/src/resources/document/state/createState/createState.ts index c0f5bb7..fa4757b 100644 --- a/src/resources/document/state/createState/createState.ts +++ b/src/resources/document/state/createState/createState.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { CreateStateParams } from "./CreateStateParams"; @@ -6,7 +6,7 @@ import { CreateStateParams } from "./CreateStateParams"; export function createState( this: XAPI, params: CreateStateParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag && params.matchHeader) headers[params.matchHeader] = params.etag; diff --git a/src/resources/document/state/deleteState/deleteState.ts b/src/resources/document/state/deleteState/deleteState.ts index 7d5fd3d..e667257 100644 --- a/src/resources/document/state/deleteState/deleteState.ts +++ b/src/resources/document/state/deleteState/deleteState.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { DeleteStateParams } from "./DeleteStateParams"; @@ -6,7 +6,7 @@ import { DeleteStateParams } from "./DeleteStateParams"; export function deleteState( this: XAPI, params: DeleteStateParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers["If-Match"] = params.etag; return this.requestResource({ diff --git a/src/resources/document/state/deleteStates/deleteStates.ts b/src/resources/document/state/deleteStates/deleteStates.ts index 77e5895..73160cc 100644 --- a/src/resources/document/state/deleteStates/deleteStates.ts +++ b/src/resources/document/state/deleteStates/deleteStates.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { DeleteStatesParams } from "./DeleteStatesParams"; @@ -6,7 +6,7 @@ import { DeleteStatesParams } from "./DeleteStatesParams"; export function deleteStates( this: XAPI, params: DeleteStatesParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag) headers["If-Match"] = params.etag; return this.requestResource({ diff --git a/src/resources/document/state/getState/getState.ts b/src/resources/document/state/getState/getState.ts index 7ad62a4..347c8b3 100644 --- a/src/resources/document/state/getState/getState.ts +++ b/src/resources/document/state/getState/getState.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { GetStateParams } from "./GetStateParams"; @@ -7,7 +7,7 @@ import { Document } from "../../Document"; export function getState( this: XAPI, params: GetStateParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.STATE, queryParams: { diff --git a/src/resources/document/state/getStates/getStates.ts b/src/resources/document/state/getStates/getStates.ts index 145dd95..8a683d8 100644 --- a/src/resources/document/state/getStates/getStates.ts +++ b/src/resources/document/state/getStates/getStates.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { GetStatesParams } from "./GetStatesParams"; @@ -6,7 +6,7 @@ import { GetStatesParams } from "./GetStatesParams"; export function getStates( this: XAPI, params: GetStatesParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.STATE, queryParams: { diff --git a/src/resources/document/state/setState/setState.ts b/src/resources/document/state/setState/setState.ts index 59821e8..d348f67 100644 --- a/src/resources/document/state/setState/setState.ts +++ b/src/resources/document/state/setState/setState.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../../adapters"; import { Resources } from "../../../../constants"; import XAPI from "../../../../XAPI"; import { SetStateParams } from "./SetStateParams"; @@ -6,7 +6,7 @@ import { SetStateParams } from "./SetStateParams"; export function setState( this: XAPI, params: SetStateParams -): AxiosPromise { +): AdapterPromise { const headers = {}; if (params.etag && params.matchHeader) headers[params.matchHeader] = params.etag; diff --git a/src/resources/statement/getMoreStatements/getMoreStatements.ts b/src/resources/statement/getMoreStatements/getMoreStatements.ts index 2f2e952..3e7213d 100644 --- a/src/resources/statement/getMoreStatements/getMoreStatements.ts +++ b/src/resources/statement/getMoreStatements/getMoreStatements.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import XAPI from "../../../XAPI"; import { StatementsResponse, StatementsResponseWithAttachments } from ".."; import { GetMoreStatementsParams } from "./GetMoreStatementsParams"; @@ -6,7 +6,7 @@ import { GetMoreStatementsParams } from "./GetMoreStatementsParams"; export function getMoreStatements( this: XAPI, params: GetMoreStatementsParams -): AxiosPromise { +): AdapterPromise { const endpoint = new URL(this.endpoint); const url = `${endpoint.protocol}//${endpoint.host}${params.more}`; return this.requestURL(url); diff --git a/src/resources/statement/getStatement/getStatement.ts b/src/resources/statement/getStatement/getStatement.ts index 9b9523b..cb34dc5 100644 --- a/src/resources/statement/getStatement/getStatement.ts +++ b/src/resources/statement/getStatement/getStatement.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { StatementResponseWithAttachments, Statement } from ".."; @@ -11,17 +11,17 @@ import { export function getStatement( this: XAPI, params: GetStatementParamsWithAttachments -): AxiosPromise; +): AdapterPromise; export function getStatement( this: XAPI, params: GetStatementParamsWithoutAttachments -): AxiosPromise; +): AdapterPromise; export function getStatement( this: XAPI, params: GetStatementParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.STATEMENT, queryParams: { diff --git a/src/resources/statement/getStatements/getStatements.ts b/src/resources/statement/getStatements/getStatements.ts index f6bc5c3..c5212a3 100644 --- a/src/resources/statement/getStatements/getStatements.ts +++ b/src/resources/statement/getStatements/getStatements.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { StatementsResponseWithAttachments, StatementsResponse } from ".."; @@ -11,17 +11,17 @@ import { export function getStatements( this: XAPI, params: GetStatementsParamsWithAttachments -): AxiosPromise; +): AdapterPromise; export function getStatements( this: XAPI, params?: GetStatementsParamsWithoutAttachments -): AxiosPromise; +): AdapterPromise; export function getStatements( this: XAPI, params?: GetStatementsParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.STATEMENT, queryParams: { diff --git a/src/resources/statement/getVoidedStatement/getVoidedStatement.ts b/src/resources/statement/getVoidedStatement/getVoidedStatement.ts index 40ef978..7d1d869 100644 --- a/src/resources/statement/getVoidedStatement/getVoidedStatement.ts +++ b/src/resources/statement/getVoidedStatement/getVoidedStatement.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import XAPI from "../../../XAPI"; import { StatementResponseWithAttachments, Statement } from ".."; @@ -11,17 +11,17 @@ import { export function getVoidedStatement( this: XAPI, params: GetVoidedStatementParamsWithAttachments -): AxiosPromise; +): AdapterPromise; export function getVoidedStatement( this: XAPI, params: GetVoidedStatementParamsWithoutAttachments -): AxiosPromise; +): AdapterPromise; export function getVoidedStatement( this: XAPI, params: GetVoidedStatementParams -): AxiosPromise { +): AdapterPromise { return this.requestResource({ resource: Resources.STATEMENT, queryParams: { diff --git a/src/resources/statement/sendStatement/sendStatement.ts b/src/resources/statement/sendStatement/sendStatement.ts index 31670b4..5dd68cc 100644 --- a/src/resources/statement/sendStatement/sendStatement.ts +++ b/src/resources/statement/sendStatement/sendStatement.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import { createMultiPart, MultiPart } from "../../../internal/multiPart"; import XAPI from "../../../XAPI"; @@ -7,7 +7,7 @@ import { SendStatementParams } from "./SendStatementParams"; export function sendStatement( this: XAPI, params: SendStatementParams -): AxiosPromise { +): AdapterPromise { const hasAttachments = params.attachments?.length; if (hasAttachments) { const multiPart: MultiPart = createMultiPart( diff --git a/src/resources/statement/sendStatements/sendStatements.ts b/src/resources/statement/sendStatements/sendStatements.ts index 7dda789..a564567 100644 --- a/src/resources/statement/sendStatements/sendStatements.ts +++ b/src/resources/statement/sendStatements/sendStatements.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Resources } from "../../../constants"; import { createMultiPart, MultiPart } from "../../../internal/multiPart"; import XAPI from "../../../XAPI"; @@ -7,7 +7,7 @@ import { SendStatementsParams } from "./SendStatementsParams"; export function sendStatements( this: XAPI, params: SendStatementsParams -): AxiosPromise { +): AdapterPromise { const hasAttachments = params.attachments?.length; if (hasAttachments) { const multiPart: MultiPart = createMultiPart( diff --git a/src/resources/statement/voidStatement/voidStatement.ts b/src/resources/statement/voidStatement/voidStatement.ts index 00823a7..1f877b6 100644 --- a/src/resources/statement/voidStatement/voidStatement.ts +++ b/src/resources/statement/voidStatement/voidStatement.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Verbs } from "../../../constants"; import XAPI from "../../../XAPI"; import { Statement } from ".."; @@ -7,7 +7,7 @@ import { VoidStatementParams } from "./VoidStatementParams"; export function voidStatement( this: XAPI, params: VoidStatementParams -): AxiosPromise { +): AdapterPromise { const voidStatement: Statement = { actor: params.actor, verb: Verbs.VOIDED, diff --git a/src/resources/statement/voidStatements/voidStatements.ts b/src/resources/statement/voidStatements/voidStatements.ts index ef3ed83..8f7ba1c 100644 --- a/src/resources/statement/voidStatements/voidStatements.ts +++ b/src/resources/statement/voidStatements/voidStatements.ts @@ -1,4 +1,4 @@ -import { AxiosPromise } from "axios"; +import { AdapterPromise } from "../../../adapters"; import { Verbs } from "../../../constants"; import XAPI from "../../../XAPI"; import { Statement } from ".."; @@ -7,7 +7,7 @@ import { VoidStatementsParams } from "./VoidStatementsParams"; export function voidStatements( this: XAPI, params: VoidStatementsParams -): AxiosPromise { +): AdapterPromise { const voidStatements: Statement[] = params.statementIds.map((statementId) => { return { actor: params.actor, From 9e555a63c4ed4307f04ea80ce51195b0984a6a4e Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Thu, 28 Mar 2024 08:16:45 +0000 Subject: [PATCH 03/20] test: Update tests with mocked adapters, add node-fetch jest project --- jest.config.js | 11 +++++++-- src/XAPI.unit.test.ts | 24 +++++++------------ .../getXAPILaunchData.unit.test.ts | 10 ++++---- .../about/getAbout/getAbout.unit.test.ts | 12 +++++----- .../getActivity/getActivity.unit.test.ts | 12 +++++----- .../agents/getAgent/getAgent.unit.test.ts | 12 +++++----- .../createActivityProfile.unit.test.ts | 12 +++++----- .../deleteActivityProfile.unit.test.ts | 12 +++++----- .../getActivityProfile.unit.test.ts | 12 +++++----- .../getActivityProfiles.unit.test.ts | 15 ++++++------ .../setActivityProfile.unit.test.ts | 12 +++++----- .../createAgentProfile.unit.test.ts | 12 +++++----- .../deleteAgentProfile.unit.test.ts | 12 +++++----- .../getAgentProfile.unit.test.ts | 12 +++++----- .../getAgentProfiles.unit.test.ts | 15 ++++++------ .../setAgentProfile.unit.test.ts | 12 +++++----- .../createState/createState.unit.test.ts | 15 ++++++------ .../deleteState/deleteState.unit.test.ts | 15 ++++++------ .../deleteStates/deleteStates.unit.test.ts | 15 ++++++------ .../state/getState/getState.unit.test.ts | 15 ++++++------ .../state/getStates/getStates.unit.test.ts | 18 +++++++------- .../state/setState/setState.unit.test.ts | 18 +++++++------- .../getMoreStatements.unit.test.ts | 9 ++++--- .../getStatement/getStatement.unit.test.ts | 18 +++++++------- .../getStatements/getStatements.unit.test.ts | 18 +++++++------- .../getVoidedStatement.unit.test.ts | 18 +++++++------- .../sendStatement/sendStatement.unit.test.ts | 12 +++++----- .../sendStatements.unit.test.ts | 12 +++++----- .../voidStatement/voidStatement.unit.test.ts | 9 ++++--- .../voidStatements.unit.test.ts | 9 ++++--- test/setupAxios.ts | 5 ++++ test/setupFetch.ts | 6 +++++ 32 files changed, 220 insertions(+), 199 deletions(-) create mode 100644 test/setupAxios.ts create mode 100644 test/setupFetch.ts diff --git a/jest.config.js b/jest.config.js index f0d62a0..4269955 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,12 +13,19 @@ module.exports = { }, projects: [ { - displayName: "dom", + displayName: "dom-axios", testEnvironment: "jsdom", + setupFiles: ["./test/setupAxios.ts"], }, { - displayName: "node", + displayName: "node-axios", testEnvironment: "node", + setupFiles: ["./test/setupAxios.ts"], + }, + { + displayName: "node-fetch", + testEnvironment: "node", + setupFiles: ["./test/setupFetch.ts"], }, ], }; diff --git a/src/XAPI.unit.test.ts b/src/XAPI.unit.test.ts index 40fa2d5..9987822 100644 --- a/src/XAPI.unit.test.ts +++ b/src/XAPI.unit.test.ts @@ -1,14 +1,12 @@ import { testEndpoint } from "../test/constants"; import XAPI from "./XAPI"; -import axios from "axios"; import { toBasicAuth } from "./helpers/toBasicAuth/toBasicAuth"; import { Versions } from "./constants"; -jest.mock("axios"); - describe("xapi constructor", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -18,9 +16,10 @@ describe("xapi constructor", () => { test("can be constructed with an endpoint", () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); xapi.getAbout(); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ headers: expect.objectContaining({ Authorization: toBasicAuth("", ""), @@ -33,9 +32,10 @@ describe("xapi constructor", () => { const xapi = new XAPI({ endpoint: testEndpoint, auth: "test", + adapter: global.adapter, }); xapi.getAbout(); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ headers: expect.objectContaining({ Authorization: "test", @@ -48,9 +48,10 @@ describe("xapi constructor", () => { const xapi = new XAPI({ endpoint: testEndpoint, version: "1.0.0", + adapter: global.adapter, }); xapi.getAbout(); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ headers: expect.objectContaining({ "X-Experience-API-Version": "1.0.0" as Versions, @@ -58,13 +59,4 @@ describe("xapi constructor", () => { }) ); }); - - test("can return the internal axios instance", () => { - const xapi = new XAPI({ - endpoint: testEndpoint, - version: "1.0.0", - }); - const axiosStatic = xapi.getAxios(); - expect(axiosStatic).toEqual(axios); - }); }); diff --git a/src/helpers/getXAPILaunchData/getXAPILaunchData.unit.test.ts b/src/helpers/getXAPILaunchData/getXAPILaunchData.unit.test.ts index f4b75fb..9692e8b 100644 --- a/src/helpers/getXAPILaunchData/getXAPILaunchData.unit.test.ts +++ b/src/helpers/getXAPILaunchData/getXAPILaunchData.unit.test.ts @@ -1,10 +1,7 @@ import { getXAPILaunchData } from "./getXAPILaunchData"; import { testIf, isNode } from "../../../test/jestUtils"; -import axios from "axios"; import { XAPILaunchParameters } from "./XAPILaunchParameters"; -jest.mock("axios"); - testIf(isNode())("return error in node environment", () => { return getXAPILaunchData().catch((error: Error) => { return expect(error.message).toBe( @@ -42,15 +39,16 @@ testIf(!isNode())("can request launch data from url", async () => { }, }); - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, }); const launchURL: URL = new URL(params.xAPILaunchService); launchURL.pathname += `launch/${params.xAPILaunchKey}`; - await getXAPILaunchData(); - expect(axios.request).toHaveBeenCalledWith( + await getXAPILaunchData({ adapter: global.adapter }); + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: launchURL.toString(), diff --git a/src/resources/about/getAbout/getAbout.unit.test.ts b/src/resources/about/getAbout/getAbout.unit.test.ts index 0fc2cd6..2c4d03a 100644 --- a/src/resources/about/getAbout/getAbout.unit.test.ts +++ b/src/resources/about/getAbout/getAbout.unit.test.ts @@ -1,13 +1,11 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testEndpoint } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("about resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -17,9 +15,10 @@ describe("about resource", () => { test("can get about", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAbout(); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.ABOUT}`, @@ -30,11 +29,12 @@ describe("about resource", () => { test("can get about with cache buster", () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); xapi.getAbout({ useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ url: expect.stringContaining( `${testEndpoint}${Resources.ABOUT}?cachebuster=` diff --git a/src/resources/activities/getActivity/getActivity.unit.test.ts b/src/resources/activities/getActivity/getActivity.unit.test.ts index 6a84ba5..5917686 100644 --- a/src/resources/activities/getActivity/getActivity.unit.test.ts +++ b/src/resources/activities/getActivity/getActivity.unit.test.ts @@ -1,13 +1,11 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testActivity, testEndpoint } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("activities resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -17,11 +15,12 @@ describe("activities resource", () => { test("can get activity", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivity({ activityId: testActivity.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -34,12 +33,13 @@ describe("activities resource", () => { test("can get activity with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivity({ activityId: testActivity.id, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/agents/getAgent/getAgent.unit.test.ts b/src/resources/agents/getAgent/getAgent.unit.test.ts index 71aca99..12e4039 100644 --- a/src/resources/agents/getAgent/getAgent.unit.test.ts +++ b/src/resources/agents/getAgent/getAgent.unit.test.ts @@ -1,14 +1,12 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("agent resource", () => { describe("get agent", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -18,11 +16,12 @@ describe("agent resource", () => { test("can get person by agent", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgent({ agent: testAgent, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.AGENTS}?agent=${encodeURIComponent( @@ -35,12 +34,13 @@ describe("agent resource", () => { test("can get person by agent with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgent({ agent: testAgent, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.unit.test.ts b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.unit.test.ts index 57ecf98..3db1f63 100644 --- a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.unit.test.ts +++ b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testDocument, @@ -8,11 +7,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("activity profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,13 +20,14 @@ describe("activity profile resource", () => { test("can create activity profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.createActivityProfile({ activityId: testActivity.id, profileId: testProfileId, profile: testDocument, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${ @@ -44,6 +43,7 @@ describe("activity profile resource", () => { test("can create activity profile with etag and match header", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -54,7 +54,7 @@ describe("activity profile resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", headers: expect.objectContaining({ diff --git a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.unit.test.ts b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.unit.test.ts index c68813d..aab11c4 100644 --- a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.unit.test.ts +++ b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testEndpoint, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("activity profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("activity profile resource", () => { test("can delete an activity profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.deleteActivityProfile({ activityId: testActivity.id, profileId: testProfileId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${ @@ -41,6 +40,7 @@ describe("activity profile resource", () => { test("can delete an activity profile with etag and match header", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; await xapi.deleteActivityProfile({ @@ -48,7 +48,7 @@ describe("activity profile resource", () => { profileId: testProfileId, etag: testEtag, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${ diff --git a/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.unit.test.ts b/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.unit.test.ts index da34de1..67423a6 100644 --- a/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.unit.test.ts +++ b/src/resources/document/activityProfile/getActivityProfile/getActivityProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testEndpoint, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("activity profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("activity profile resource", () => { test("can get an activity profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivityProfile({ activityId: testActivity.id, profileId: testProfileId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -41,13 +40,14 @@ describe("activity profile resource", () => { test("can get an activity profile with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivityProfile({ activityId: testActivity.id, profileId: testProfileId, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.unit.test.ts b/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.unit.test.ts index 936433b..543a8e7 100644 --- a/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.unit.test.ts +++ b/src/resources/document/activityProfile/getActivityProfiles/getActivityProfiles.unit.test.ts @@ -1,13 +1,11 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testEndpoint } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("activity profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -17,11 +15,12 @@ describe("activity profile resource", () => { test("can get all activity profiles", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivityProfiles({ activityId: testActivity.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -34,6 +33,7 @@ describe("activity profile resource", () => { test("can get all activity profiles since a certain date", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const since = new Date(); since.setDate(since.getDate() - 1); // yesterday @@ -41,7 +41,7 @@ describe("activity profile resource", () => { activityId: testActivity.id, since: since.toISOString(), }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -56,12 +56,13 @@ describe("activity profile resource", () => { test("can get all activity profiles with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getActivityProfiles({ activityId: testActivity.id, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.unit.test.ts b/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.unit.test.ts index 37a96e1..44a5e68 100644 --- a/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.unit.test.ts +++ b/src/resources/document/activityProfile/setActivityProfile/setActivityProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testDocument, @@ -9,11 +8,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("activity profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -23,6 +21,7 @@ describe("activity profile resource", () => { test("can set activity profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -33,7 +32,7 @@ describe("activity profile resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", headers: expect.objectContaining({ @@ -52,6 +51,7 @@ describe("activity profile resource", () => { test("can set activity profile with text/plain content type", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -64,7 +64,7 @@ describe("activity profile resource", () => { matchHeader: testMatchHeader, contentType: plainTextContentType, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", headers: expect.objectContaining({ diff --git a/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.unit.test.ts b/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.unit.test.ts index 6eccca8..209f8e7 100644 --- a/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.unit.test.ts +++ b/src/resources/document/agentProfile/createAgentProfile/createAgentProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testAgent, testDocument, @@ -8,11 +7,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("agent profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,13 +20,14 @@ describe("agent profile resource", () => { test("can create agent profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.createAgentProfile({ agent: testAgent, profileId: testProfileId, profile: testDocument, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${ @@ -44,6 +43,7 @@ describe("agent profile resource", () => { test("can create agent profile with an etag", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -54,7 +54,7 @@ describe("agent profile resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${ diff --git a/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.unit.test.ts b/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.unit.test.ts index 827fb7e..ede1b34 100644 --- a/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.unit.test.ts +++ b/src/resources/document/agentProfile/deleteAgentProfile/deleteAgentProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("agent profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("agent profile resource", () => { test("can delete an agent profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.deleteAgentProfile({ agent: testAgent, profileId: testProfileId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${ @@ -41,6 +40,7 @@ describe("agent profile resource", () => { test("can delete an agent profile with an etag", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; await xapi.deleteAgentProfile({ @@ -48,7 +48,7 @@ describe("agent profile resource", () => { profileId: testProfileId, etag: testEtag, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${ diff --git a/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.unit.test.ts b/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.unit.test.ts index c90806a..254cebf 100644 --- a/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.unit.test.ts +++ b/src/resources/document/agentProfile/getAgentProfile/getAgentProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("agent profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("agent profile resource", () => { test("can get an agent profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgentProfile({ agent: testAgent, profileId: testProfileId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -41,13 +40,14 @@ describe("agent profile resource", () => { test("can get an agent profile with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgentProfile({ agent: testAgent, profileId: testProfileId, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.unit.test.ts b/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.unit.test.ts index 0937937..5b44f0c 100644 --- a/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.unit.test.ts +++ b/src/resources/document/agentProfile/getAgentProfiles/getAgentProfiles.unit.test.ts @@ -1,13 +1,11 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("agent profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -17,11 +15,12 @@ describe("agent profile resource", () => { test("can get all agent profiles", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgentProfiles({ agent: testAgent, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -34,6 +33,7 @@ describe("agent profile resource", () => { test("can get all agent profiles since a certain date", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const since = new Date(); since.setDate(since.getDate() - 1); // yesterday @@ -41,7 +41,7 @@ describe("agent profile resource", () => { agent: testAgent, since: since.toISOString(), }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -56,12 +56,13 @@ describe("agent profile resource", () => { test("can get all agent profiles with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getAgentProfiles({ agent: testAgent, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.unit.test.ts b/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.unit.test.ts index 82a230d..f4b8eaf 100644 --- a/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.unit.test.ts +++ b/src/resources/document/agentProfile/setAgentProfile/setAgentProfile.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testAgent, testDocument, @@ -8,11 +7,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("agent profile resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,6 +20,7 @@ describe("agent profile resource", () => { test("can set agent profile", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -32,7 +31,7 @@ describe("agent profile resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", url: `${testEndpoint}${ @@ -51,6 +50,7 @@ describe("agent profile resource", () => { test("can set agent profile with content type", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -63,7 +63,7 @@ describe("agent profile resource", () => { matchHeader: testMatchHeader, contentType: plainTextContentType, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", url: `${testEndpoint}${ diff --git a/src/resources/document/state/createState/createState.unit.test.ts b/src/resources/document/state/createState/createState.unit.test.ts index 6343c24..c92f2fd 100644 --- a/src/resources/document/state/createState/createState.unit.test.ts +++ b/src/resources/document/state/createState/createState.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -9,11 +8,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -23,6 +21,7 @@ describe("state resource", () => { test("can create state", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.createState({ agent: testAgent, @@ -30,7 +29,7 @@ describe("state resource", () => { stateId: testStateId, state: testDocument, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -46,6 +45,7 @@ describe("state resource", () => { test("can create state with registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.createState({ @@ -55,7 +55,7 @@ describe("state resource", () => { state: testDocument, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -73,6 +73,7 @@ describe("state resource", () => { test("can create state with etag and match header", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -84,7 +85,7 @@ describe("state resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", headers: expect.objectContaining({ diff --git a/src/resources/document/state/deleteState/deleteState.unit.test.ts b/src/resources/document/state/deleteState/deleteState.unit.test.ts index 581afeb..79ab541 100644 --- a/src/resources/document/state/deleteState/deleteState.unit.test.ts +++ b/src/resources/document/state/deleteState/deleteState.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -8,11 +7,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,13 +20,14 @@ describe("state resource", () => { test("can delete a state", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.deleteState({ agent: testAgent, activityId: testActivity.id, stateId: testStateId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -43,6 +42,7 @@ describe("state resource", () => { test("can delete a state with registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.deleteState({ @@ -51,7 +51,7 @@ describe("state resource", () => { stateId: testStateId, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -68,6 +68,7 @@ describe("state resource", () => { test("can delete a state with etag", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; await xapi.deleteState({ @@ -76,7 +77,7 @@ describe("state resource", () => { stateId: testStateId, etag: testEtag, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", headers: expect.objectContaining({ diff --git a/src/resources/document/state/deleteStates/deleteStates.unit.test.ts b/src/resources/document/state/deleteStates/deleteStates.unit.test.ts index 1011ddc..ecbbd5f 100644 --- a/src/resources/document/state/deleteStates/deleteStates.unit.test.ts +++ b/src/resources/document/state/deleteStates/deleteStates.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("state resource", () => { test("can delete all states", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.deleteStates({ agent: testAgent, activityId: testActivity.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -39,6 +38,7 @@ describe("state resource", () => { test("can delete all state for a registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.deleteStates({ @@ -46,7 +46,7 @@ describe("state resource", () => { activityId: testActivity.id, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -61,6 +61,7 @@ describe("state resource", () => { test("can delete all states with etag", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; await xapi.deleteStates({ @@ -68,7 +69,7 @@ describe("state resource", () => { activityId: testActivity.id, etag: testEtag, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "DELETE", headers: expect.objectContaining({ diff --git a/src/resources/document/state/getState/getState.unit.test.ts b/src/resources/document/state/getState/getState.unit.test.ts index 226830b..e021731 100644 --- a/src/resources/document/state/getState/getState.unit.test.ts +++ b/src/resources/document/state/getState/getState.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -8,11 +7,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,13 +20,14 @@ describe("state resource", () => { test("can get a state", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getState({ agent: testAgent, activityId: testActivity.id, stateId: testStateId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -43,6 +42,7 @@ describe("state resource", () => { test("can get a state with a registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.getState({ @@ -51,7 +51,7 @@ describe("state resource", () => { stateId: testStateId, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -68,6 +68,7 @@ describe("state resource", () => { test("can get a state with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getState({ agent: testAgent, @@ -75,7 +76,7 @@ describe("state resource", () => { stateId: testStateId, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/state/getStates/getStates.unit.test.ts b/src/resources/document/state/getStates/getStates.unit.test.ts index 8253858..379926a 100644 --- a/src/resources/document/state/getStates/getStates.unit.test.ts +++ b/src/resources/document/state/getStates/getStates.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -7,11 +6,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -21,12 +19,13 @@ describe("state resource", () => { test("can get all states", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getStates({ agent: testAgent, activityId: testActivity.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -39,6 +38,7 @@ describe("state resource", () => { test("can get all states for a registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.getStates({ @@ -46,7 +46,7 @@ describe("state resource", () => { activityId: testActivity.id, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -61,6 +61,7 @@ describe("state resource", () => { test("can get all states since a certain date", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const since = new Date(); since.setDate(since.getDate() - 1); // yesterday @@ -69,7 +70,7 @@ describe("state resource", () => { activityId: testActivity.id, since: since.toISOString(), }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -84,13 +85,14 @@ describe("state resource", () => { test("can get all states with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getStates({ agent: testAgent, activityId: testActivity.id, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/document/state/setState/setState.unit.test.ts b/src/resources/document/state/setState/setState.unit.test.ts index bdfaa8e..6073b42 100644 --- a/src/resources/document/state/setState/setState.unit.test.ts +++ b/src/resources/document/state/setState/setState.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -9,11 +8,10 @@ import { } from "../../../../../test/constants"; import { Resources } from "../../../../constants"; -jest.mock("axios"); - describe("state resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -23,6 +21,7 @@ describe("state resource", () => { test("can set state", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.setState({ agent: testAgent, @@ -30,7 +29,7 @@ describe("state resource", () => { stateId: testStateId, state: testDocument, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -46,6 +45,7 @@ describe("state resource", () => { test("can set state with content type", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const plainTextContentType = "text/plain"; await xapi.setState({ @@ -55,7 +55,7 @@ describe("state resource", () => { state: testDocument.test, contentType: plainTextContentType, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", headers: expect.objectContaining({ @@ -74,6 +74,7 @@ describe("state resource", () => { test("can set state with registration", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; await xapi.setState({ @@ -83,7 +84,7 @@ describe("state resource", () => { state: testDocument, registration: testRegistration, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", url: `${testEndpoint}${Resources.STATE}?agent=${encodeURIComponent( @@ -101,6 +102,7 @@ describe("state resource", () => { test("can set state with etag and match header", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testEtag = "my-etag"; const testMatchHeader = "If-Match"; @@ -112,7 +114,7 @@ describe("state resource", () => { etag: testEtag, matchHeader: testMatchHeader, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "PUT", headers: expect.objectContaining({ diff --git a/src/resources/statement/getMoreStatements/getMoreStatements.unit.test.ts b/src/resources/statement/getMoreStatements/getMoreStatements.unit.test.ts index 60f70b9..6fe93ae 100644 --- a/src/resources/statement/getMoreStatements/getMoreStatements.unit.test.ts +++ b/src/resources/statement/getMoreStatements/getMoreStatements.unit.test.ts @@ -1,12 +1,10 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testEndpoint } from "../../../../test/constants"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -16,13 +14,14 @@ describe("statement resource", () => { test("can get more statements", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const endpoint = new URL(testEndpoint); const testMoreIrl = "?more=test-more-irl"; await xapi.getMoreStatements({ more: testMoreIrl, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${endpoint.protocol}//${endpoint.host}${testMoreIrl}`, diff --git a/src/resources/statement/getStatement/getStatement.unit.test.ts b/src/resources/statement/getStatement/getStatement.unit.test.ts index cc93e1c..ad060a7 100644 --- a/src/resources/statement/getStatement/getStatement.unit.test.ts +++ b/src/resources/statement/getStatement/getStatement.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAttachmentContent, testEndpoint, @@ -8,11 +7,10 @@ import { } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,12 +20,13 @@ describe("statement resource", () => { test("can get a single statement", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getStatement({ statementId: testStatementId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?statementId=${testStatementId}`, @@ -37,7 +36,8 @@ describe("statement resource", () => { test("can get a single statement with attachments", async () => { jest.resetAllMocks(); - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "multipart/mixed; boundary=", }, @@ -45,13 +45,14 @@ describe("statement resource", () => { }); const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; const result = await xapi.getStatement({ statementId: testStatementId, attachments: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?statementId=${testStatementId}&attachments=true`, @@ -64,13 +65,14 @@ describe("statement resource", () => { test("can get a single statement with chosen format", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getStatement({ statementId: testStatementId, format: "canonical", }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?statementId=${testStatementId}&format=canonical`, diff --git a/src/resources/statement/getStatements/getStatements.unit.test.ts b/src/resources/statement/getStatements/getStatements.unit.test.ts index 2c55dac..b11a45c 100644 --- a/src/resources/statement/getStatements/getStatements.unit.test.ts +++ b/src/resources/statement/getStatements/getStatements.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testActivity, testAgent, @@ -8,11 +7,10 @@ import { } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,9 +20,10 @@ describe("statement resource", () => { test("can get multiple statements", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getStatements(); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}`, @@ -35,11 +34,12 @@ describe("statement resource", () => { test("can get multiple statements with attachments", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getStatements({ attachments: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?attachments=true`, @@ -50,6 +50,7 @@ describe("statement resource", () => { test("can get multiple statements with all query parameters", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testRegistration = "test-registration"; const since = new Date(); @@ -68,7 +69,7 @@ describe("statement resource", () => { until: since.toISOString(), verb: testVerb.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${ @@ -89,11 +90,12 @@ describe("statement resource", () => { test("can get multiple statements with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.getStatements({ useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/statement/getVoidedStatement/getVoidedStatement.unit.test.ts b/src/resources/statement/getVoidedStatement/getVoidedStatement.unit.test.ts index 3e497e6..076852b 100644 --- a/src/resources/statement/getVoidedStatement/getVoidedStatement.unit.test.ts +++ b/src/resources/statement/getVoidedStatement/getVoidedStatement.unit.test.ts @@ -1,13 +1,11 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testEndpoint } from "../../../../test/constants"; import { Resources } from "../../../constants"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -17,12 +15,13 @@ describe("statement resource", () => { test("can get a voided statement", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getVoidedStatement({ voidedStatementId: testStatementId, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?voidedStatementId=${testStatementId}`, @@ -33,13 +32,14 @@ describe("statement resource", () => { test("can get a voided statement with attachments", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getVoidedStatement({ voidedStatementId: testStatementId, attachments: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?voidedStatementId=${testStatementId}&attachments=true`, @@ -50,13 +50,14 @@ describe("statement resource", () => { test("can get a voided statement with chosen format", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getVoidedStatement({ voidedStatementId: testStatementId, format: "canonical", }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: `${testEndpoint}${Resources.STATEMENT}?voidedStatementId=${testStatementId}&format=canonical`, @@ -67,13 +68,14 @@ describe("statement resource", () => { test("can get a voided statement with cache buster", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); const testStatementId = "test-statement-id"; await xapi.getVoidedStatement({ voidedStatementId: testStatementId, useCacheBuster: true, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", url: expect.stringContaining( diff --git a/src/resources/statement/sendStatement/sendStatement.unit.test.ts b/src/resources/statement/sendStatement/sendStatement.unit.test.ts index c990b22..72482ce 100644 --- a/src/resources/statement/sendStatement/sendStatement.unit.test.ts +++ b/src/resources/statement/sendStatement/sendStatement.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAttachmentArrayBuffer, testEndpoint, @@ -10,11 +9,10 @@ import { Resources } from "../../../constants"; import { createMultiPart } from "../../../internal/multiPart"; import { testIf, isNode } from "../../../../test/jestUtils"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -24,11 +22,12 @@ describe("statement resource", () => { test("can send a statement", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.sendStatement({ statement: testStatement, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATEMENT}`, @@ -42,12 +41,13 @@ describe("statement resource", () => { async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.sendStatement({ statement: testStatementWithEmbeddedAttachments, attachments: [testAttachmentArrayBuffer], }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", headers: expect.objectContaining({ diff --git a/src/resources/statement/sendStatements/sendStatements.unit.test.ts b/src/resources/statement/sendStatements/sendStatements.unit.test.ts index 4a05396..65f2d14 100644 --- a/src/resources/statement/sendStatements/sendStatements.unit.test.ts +++ b/src/resources/statement/sendStatements/sendStatements.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAttachmentArrayBuffer, testEndpoint, @@ -10,11 +9,10 @@ import { Resources } from "../../../constants"; import { createMultiPart } from "../../../internal/multiPart"; import { testIf, isNode } from "../../../../test/jestUtils"; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -24,11 +22,12 @@ describe("statement resource", () => { test("can send multiple statements", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.sendStatements({ statements: [testStatement, testStatement], }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATEMENT}`, @@ -42,6 +41,7 @@ describe("statement resource", () => { async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.sendStatements({ statements: [ @@ -50,7 +50,7 @@ describe("statement resource", () => { ], attachments: [testAttachmentArrayBuffer, testAttachmentArrayBuffer], }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", headers: expect.objectContaining({ diff --git a/src/resources/statement/voidStatement/voidStatement.unit.test.ts b/src/resources/statement/voidStatement/voidStatement.unit.test.ts index c8cf3cf..97cebe4 100644 --- a/src/resources/statement/voidStatement/voidStatement.unit.test.ts +++ b/src/resources/statement/voidStatement/voidStatement.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint, @@ -8,11 +7,10 @@ import { import { Resources, Verbs } from "../../../constants"; import { Statement } from ".."; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -22,12 +20,13 @@ describe("statement resource", () => { test("can void a statement", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.voidStatement({ actor: testAgent, statementId: testStatement.id, }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATEMENT}`, diff --git a/src/resources/statement/voidStatements/voidStatements.unit.test.ts b/src/resources/statement/voidStatements/voidStatements.unit.test.ts index 983e6e6..997576e 100644 --- a/src/resources/statement/voidStatements/voidStatements.unit.test.ts +++ b/src/resources/statement/voidStatements/voidStatements.unit.test.ts @@ -1,5 +1,4 @@ import XAPI from "../../../XAPI"; -import axios from "axios"; import { testAgent, testEndpoint, @@ -9,11 +8,10 @@ import { import { Resources, Verbs } from "../../../constants"; import { Statement } from ".."; -jest.mock("axios"); - describe("statement resource", () => { beforeEach(() => { - (axios as jest.MockedFunction).request.mockResolvedValueOnce({ + global.adapterFn.mockClear(); + global.adapterFn.mockResolvedValueOnce({ headers: { "content-type": "application/json", }, @@ -23,12 +21,13 @@ describe("statement resource", () => { test("can void multiple statements", async () => { const xapi = new XAPI({ endpoint: testEndpoint, + adapter: global.adapter, }); await xapi.voidStatements({ actor: testAgent, statementIds: [testStatement.id, testStatementWithEmbeddedAttachments.id], }); - expect(axios.request).toHaveBeenCalledWith( + expect(global.adapterFn).toHaveBeenCalledWith( expect.objectContaining({ method: "POST", url: `${testEndpoint}${Resources.STATEMENT}`, diff --git a/test/setupAxios.ts b/test/setupAxios.ts new file mode 100644 index 0000000..a746cc5 --- /dev/null +++ b/test/setupAxios.ts @@ -0,0 +1,5 @@ +import axiosAdapter from "../src/adapters/axiosAdapter"; + +jest.mock("../src/adapters/axiosAdapter"); + +global.adapterFn = axiosAdapter; diff --git a/test/setupFetch.ts b/test/setupFetch.ts new file mode 100644 index 0000000..4934b00 --- /dev/null +++ b/test/setupFetch.ts @@ -0,0 +1,6 @@ +import fetchAdapter from "../src/adapters/fetchAdapter"; + +jest.mock("../src/adapters/fetchAdapter"); + +global.adapter = "fetch"; +global.adapterFn = fetchAdapter; From ae8ef48f45adacb0f3b4f9169daf9729fbe0821f Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:21:26 +0100 Subject: [PATCH 04/20] test: Adjust integration tests to handle adapters --- jest.config.js | 53 +++++++++++++++++++++++++++++++++++++----- package-lock.json | 9 ++++++- package.json | 7 +++--- src/XAPI.int.test.ts | 1 + test/getCredentials.ts | 1 + test/polyfillFetch.ts | 1 + test/setupAxios.ts | 6 ++--- test/setupFetch.ts | 6 ++--- test/setupInt.ts | 4 ++++ test/setupUnit.ts | 22 ++++++++++++++++++ 10 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 test/polyfillFetch.ts create mode 100644 test/setupInt.ts create mode 100644 test/setupUnit.ts diff --git a/jest.config.js b/jest.config.js index 4269955..bceedb9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,19 +13,60 @@ module.exports = { }, projects: [ { - displayName: "dom-axios", + displayName: "dom-axios-unit", + testMatch: ["**/*.unit.test.ts"], testEnvironment: "jsdom", - setupFiles: ["./test/setupAxios.ts"], + setupFiles: ["./test/setupUnit.ts", "./test/setupAxios.ts"], }, { - displayName: "node-axios", + displayName: "dom-fetch-unit", + testMatch: ["**/*.unit.test.ts"], + testEnvironment: "jsdom", + setupFiles: [ + "./test/setupUnit.ts", + "./test/polyfillFetch.ts", + "./test/setupFetch.ts", + ], + }, + { + displayName: "node-axios-unit", + testMatch: ["**/*.unit.test.ts"], + testEnvironment: "node", + setupFiles: ["./test/setupUnit.ts", "./test/setupAxios.ts"], + }, + { + displayName: "node-fetch-unit", + testMatch: ["**/*.unit.test.ts"], + testEnvironment: "node", + setupFiles: ["./test/setupUnit.ts", "./test/setupFetch.ts"], + }, + { + displayName: "dom-axios-int", + testMatch: ["**/*.int.test.ts"], + testEnvironment: "jsdom", + setupFilesAfterEnv: ["./test/setupInt.ts"], + }, + { + displayName: "dom-fetch-int", + testMatch: ["**/*.int.test.ts"], + testEnvironment: "jsdom", + setupFilesAfterEnv: [ + "./test/setupInt.ts", + "./test/polyfillFetch.ts", + "./test/setupFetch.ts", + ], + }, + { + displayName: "node-axios-int", + testMatch: ["**/*.int.test.ts"], testEnvironment: "node", - setupFiles: ["./test/setupAxios.ts"], + setupFilesAfterEnv: ["./test/setupInt.ts", "./test/setupAxios.ts"], }, { - displayName: "node-fetch", + displayName: "node-fetch-int", + testMatch: ["**/*.int.test.ts"], testEnvironment: "node", - setupFiles: ["./test/setupFetch.ts"], + setupFilesAfterEnv: ["./test/setupInt.ts", "./test/setupFetch.ts"], }, ], }; diff --git a/package-lock.json b/package-lock.json index b378f99..04c8537 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,8 @@ "rollup": "^4.3.0", "ts-jest": "^29.1.1", "typescript": "^5.2.2", - "uuid": "^9.0.1" + "uuid": "^9.0.1", + "whatwg-fetch": "^3.6.20" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -9607,6 +9608,12 @@ "node": ">=12" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", diff --git a/package.json b/package.json index 36190e9..6928070 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "build": "npm run clean && npm run build:types && npm run build:js", "format": "prettier --write '**/*.{js,jsx,json,ts,tsx}'", "test": "jest --runInBand && npm run test:example:node:require && npm run test:example:node:import", - "test:unit": "jest --testPathPattern=.unit.test.ts", - "test:int": "jest --testPathPattern=.int.test.ts --runInBand", + "test:unit": "jest --selectProjects dom-axios-unit dom-fetch-unit node-axios-unit node-fetch-unit", + "test:int": "jest --selectProjects dom-axios-int dom-fetch-int node-axios-int node-fetch-int --runInBand", "test:format": "prettier --check .", "test:example:node:require": "node ./example/node/require.js", "test:example:node:import": "node --experimental-modules --es-module-specifier-resolution=node ./example/node/import.mjs", @@ -66,7 +66,8 @@ "rollup": "^4.3.0", "ts-jest": "^29.1.1", "typescript": "^5.2.2", - "uuid": "^9.0.1" + "uuid": "^9.0.1", + "whatwg-fetch": "^3.6.20" }, "dependencies": { "axios": "^1.6.0" diff --git a/src/XAPI.int.test.ts b/src/XAPI.int.test.ts index f799971..92d9bc4 100644 --- a/src/XAPI.int.test.ts +++ b/src/XAPI.int.test.ts @@ -8,6 +8,7 @@ forEachLRS((_xapi, credential) => { test("can perform basic authentication challenges when no authorization process is required", () => { const noAuthXapi = new XAPI({ endpoint: endpoint, + adapter: global.adapter, }); expect(noAuthXapi.getAbout()).resolves.toBeDefined(); }); diff --git a/test/getCredentials.ts b/test/getCredentials.ts index dc13021..90ee4e4 100644 --- a/test/getCredentials.ts +++ b/test/getCredentials.ts @@ -23,6 +23,7 @@ export function forEachLRS( const xapi: XAPI = new XAPI({ endpoint: credential.endpoint, auth: auth, + adapter: global.adapter, }); callbackfn(xapi, credential); }); diff --git a/test/polyfillFetch.ts b/test/polyfillFetch.ts new file mode 100644 index 0000000..7a2808d --- /dev/null +++ b/test/polyfillFetch.ts @@ -0,0 +1 @@ +import "whatwg-fetch"; diff --git a/test/setupAxios.ts b/test/setupAxios.ts index a746cc5..5d1cfbe 100644 --- a/test/setupAxios.ts +++ b/test/setupAxios.ts @@ -1,5 +1,3 @@ -import axiosAdapter from "../src/adapters/axiosAdapter"; +import * as axiosAdapter from "../src/adapters/axiosAdapter"; -jest.mock("../src/adapters/axiosAdapter"); - -global.adapterFn = axiosAdapter; +global.adapterFn = jest.spyOn(axiosAdapter, "default"); diff --git a/test/setupFetch.ts b/test/setupFetch.ts index 4934b00..7a92d49 100644 --- a/test/setupFetch.ts +++ b/test/setupFetch.ts @@ -1,6 +1,4 @@ -import fetchAdapter from "../src/adapters/fetchAdapter"; - -jest.mock("../src/adapters/fetchAdapter"); +import * as fetchAdapter from "../src/adapters/fetchAdapter"; global.adapter = "fetch"; -global.adapterFn = fetchAdapter; +global.adapterFn = jest.spyOn(fetchAdapter, "default"); diff --git a/test/setupInt.ts b/test/setupInt.ts new file mode 100644 index 0000000..5a7b917 --- /dev/null +++ b/test/setupInt.ts @@ -0,0 +1,4 @@ +beforeEach(() => { + // Add artificial delay between integration tests to avoid rate limit in SCORM Cloud + return new Promise((resolve) => setTimeout(() => resolve(null), 250)); +}); diff --git a/test/setupUnit.ts b/test/setupUnit.ts new file mode 100644 index 0000000..e35bc90 --- /dev/null +++ b/test/setupUnit.ts @@ -0,0 +1,22 @@ +import axios from "axios"; + +jest.mock("axios"); + +(axios.request as any).mockImplementation(() => + Promise.resolve({ + headers: { + "content-type": "application/json", + }, + }) +); + +global.fetch = jest.fn(() => + Promise.resolve({ + json: () => + Promise.resolve({ + headers: { + "content-type": "application/json", + }, + }), + } as Response) +); From 9e64e92a20f8f837b52e2ace25ba1da47dbed5fa Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:25:05 +0100 Subject: [PATCH 05/20] fix: Handle fetch requests and responses for different payload types --- src/adapters/fetchAdapter.ts | 37 +++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/adapters/fetchAdapter.ts b/src/adapters/fetchAdapter.ts index 1cdb2c3..6fad578 100644 --- a/src/adapters/fetchAdapter.ts +++ b/src/adapters/fetchAdapter.ts @@ -1,17 +1,36 @@ import { AdapterFunction } from "."; -const fetchAdapter: AdapterFunction = ({ url, method, data, headers }) => { - return fetch(url, { +const fetchAdapter: AdapterFunction = async ({ + url, + method, + data, + headers, +}) => { + let body: any = data; + const contentType: string | undefined = headers["Content-Type"]; + if (contentType === "application/json") { + body = JSON.stringify(body); + } + const response = await fetch(url, { method, - body: data, + body, headers, - }).then(({ headers, status, json }) => { - return json().then((data) => ({ - data, - headers, - status, - })); }); + if (!response.ok) { + const err = await response.text(); + throw new Error(err); + } + let text: any = await response.text(); + if (typeof text === "string") { + try { + text = JSON.parse(text); + } catch {} + } + return { + data: text, + headers: Object.fromEntries(response.headers.entries()), + status: response.status, + }; }; export default fetchAdapter; From 31264a41375fc1fd038b100bf823c7bf99d7bc76 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:40:38 +0100 Subject: [PATCH 06/20] test: Add axios and fetch adapter unit tests --- src/adapters/axiosAdapter.unit.test.ts | 26 ++++++++++++++++++++++++++ src/adapters/fetchAdapter.unit.test.ts | 25 +++++++++++++++++++++++++ test/setupUnit.ts | 3 +++ 3 files changed, 54 insertions(+) create mode 100644 src/adapters/axiosAdapter.unit.test.ts create mode 100644 src/adapters/fetchAdapter.unit.test.ts diff --git a/src/adapters/axiosAdapter.unit.test.ts b/src/adapters/axiosAdapter.unit.test.ts new file mode 100644 index 0000000..8871cbf --- /dev/null +++ b/src/adapters/axiosAdapter.unit.test.ts @@ -0,0 +1,26 @@ +import axios from "axios"; +import axiosAdapter from "./axiosAdapter"; + +describe("axiosAdapter", () => { + it("makes an axios network request", async () => { + axiosAdapter({ + url: "https://www.example.com", + method: "POST", + data: "foo", + headers: { + Authorization: "Basic ABCDEFG", + }, + }); + + expect(axios.request).toHaveBeenCalledWith( + expect.objectContaining({ + url: "https://www.example.com", + method: "POST", + data: "foo", + headers: { + Authorization: "Basic ABCDEFG", + }, + }) + ); + }); +}); diff --git a/src/adapters/fetchAdapter.unit.test.ts b/src/adapters/fetchAdapter.unit.test.ts new file mode 100644 index 0000000..c01a885 --- /dev/null +++ b/src/adapters/fetchAdapter.unit.test.ts @@ -0,0 +1,25 @@ +import fetchAdapter from "./fetchAdapter"; + +describe("fetchAdapter", () => { + it("makes a fetch network request", async () => { + fetchAdapter({ + url: "https://www.example.com", + method: "POST", + data: "foo", + headers: { + Authorization: "Basic ABCDEFG", + }, + }); + + expect(fetch).toHaveBeenCalledWith( + "https://www.example.com", + expect.objectContaining({ + method: "POST", + body: "foo", + headers: { + Authorization: "Basic ABCDEFG", + }, + }) + ); + }); +}); diff --git a/test/setupUnit.ts b/test/setupUnit.ts index e35bc90..9f00110 100644 --- a/test/setupUnit.ts +++ b/test/setupUnit.ts @@ -18,5 +18,8 @@ global.fetch = jest.fn(() => "content-type": "application/json", }, }), + text: () => Promise.resolve(""), + ok: true, + headers: new Headers(), } as Response) ); From 113f41b573f314e3f62a16b12846793d937a72ea Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:55:50 +0100 Subject: [PATCH 07/20] test: resolveAdapterFunction --- .../resolveAdapterFunction.unit.test.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/adapters/resolveAdapterFunction.unit.test.ts diff --git a/src/adapters/resolveAdapterFunction.unit.test.ts b/src/adapters/resolveAdapterFunction.unit.test.ts new file mode 100644 index 0000000..f918cad --- /dev/null +++ b/src/adapters/resolveAdapterFunction.unit.test.ts @@ -0,0 +1,36 @@ +import { AdapterFunction } from "."; +import axiosAdapter from "./axiosAdapter"; +import fetchAdapter from "./fetchAdapter"; +import resolveAdapterFunction from "./resolveAdapterFunction"; + +describe("resolveAdapterFunction", () => { + it("Returns axios by default", () => { + const result = resolveAdapterFunction(); + + expect(result).toBe(axiosAdapter); + }); + + it("Returns axios if provided as parameter", () => { + const result = resolveAdapterFunction("axios"); + + expect(result).toBe(axiosAdapter); + }); + + it("Returns fetch if provided as parameter", () => { + const result = resolveAdapterFunction("fetch"); + + expect(result).toBe(fetchAdapter); + }); + + it("Returns custom if function provided as parameter", () => { + const customAdapter: AdapterFunction = () => + Promise.resolve({ + data: {} as any, + headers: {}, + status: 0, + }); + const result = resolveAdapterFunction(customAdapter); + + expect(result).toBe(customAdapter); + }); +}); From 6c22845e218077196924bc5aac399edab132f71e Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:59:46 +0100 Subject: [PATCH 08/20] test: Fetch stringifies data if JSON content type --- src/adapters/fetchAdapter.unit.test.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/adapters/fetchAdapter.unit.test.ts b/src/adapters/fetchAdapter.unit.test.ts index c01a885..7f2a10f 100644 --- a/src/adapters/fetchAdapter.unit.test.ts +++ b/src/adapters/fetchAdapter.unit.test.ts @@ -22,4 +22,26 @@ describe("fetchAdapter", () => { }) ); }); + + it("stringifies JSON data", async () => { + fetchAdapter({ + url: "https://www.example.com", + method: "POST", + data: { foo: true }, + headers: { + "Content-Type": "application/json", + }, + }); + + expect(fetch).toHaveBeenCalledWith( + "https://www.example.com", + expect.objectContaining({ + method: "POST", + body: '{"foo":true}', + headers: { + "Content-Type": "application/json", + }, + }) + ); + }); }); From 7f33fcb6077831280567d35368cb6203640a594c Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:37:57 +0100 Subject: [PATCH 09/20] test: Add rejected promise check to fetchAdapter --- src/adapters/fetchAdapter.unit.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/adapters/fetchAdapter.unit.test.ts b/src/adapters/fetchAdapter.unit.test.ts index 7f2a10f..ab626c7 100644 --- a/src/adapters/fetchAdapter.unit.test.ts +++ b/src/adapters/fetchAdapter.unit.test.ts @@ -44,4 +44,23 @@ describe("fetchAdapter", () => { }) ); }); + + it("Returns a rejected promise with error message if an error is encountered", async () => { + (fetch as jest.MockedFn).mockImplementationOnce(() => + Promise.resolve({ + text: () => Promise.resolve("i am error"), + ok: false, + } as Response) + ); + + const result = fetchAdapter({ + url: "https://www.example.com", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + expect(result).rejects.toThrow("i am error"); + }); }); From dc9415954e384619a8ce6be7ed3f6a805d6c9651 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:51:07 +0100 Subject: [PATCH 10/20] test: Add fetch response gets parsed as JSON --- src/adapters/fetchAdapter.unit.test.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/adapters/fetchAdapter.unit.test.ts b/src/adapters/fetchAdapter.unit.test.ts index ab626c7..97609ab 100644 --- a/src/adapters/fetchAdapter.unit.test.ts +++ b/src/adapters/fetchAdapter.unit.test.ts @@ -45,7 +45,7 @@ describe("fetchAdapter", () => { ); }); - it("Returns a rejected promise with error message if an error is encountered", async () => { + it("Returns a rejected promise with error message if an error is encountered", () => { (fetch as jest.MockedFn).mockImplementationOnce(() => Promise.resolve({ text: () => Promise.resolve("i am error"), @@ -63,4 +63,28 @@ describe("fetchAdapter", () => { expect(result).rejects.toThrow("i am error"); }); + + it("Parses fetch text as JSON", () => { + (fetch as jest.MockedFn).mockImplementationOnce(() => + Promise.resolve({ + text: () => Promise.resolve('{"foo": true}'), + ok: true, + headers: new Headers(), + } as Response) + ); + + const result = fetchAdapter({ + url: "https://www.example.com", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + expect(result).resolves.toEqual( + expect.objectContaining({ + data: { foo: true }, + }) + ); + }); }); From 73b7627d6e6ce833b6d47af054e74c72d098e149 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:55:09 +0100 Subject: [PATCH 11/20] fix: Remove unnecessary type check for fetch response text --- src/adapters/fetchAdapter.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/adapters/fetchAdapter.ts b/src/adapters/fetchAdapter.ts index 6fad578..77dac40 100644 --- a/src/adapters/fetchAdapter.ts +++ b/src/adapters/fetchAdapter.ts @@ -20,14 +20,12 @@ const fetchAdapter: AdapterFunction = async ({ const err = await response.text(); throw new Error(err); } - let text: any = await response.text(); - if (typeof text === "string") { - try { - text = JSON.parse(text); - } catch {} - } + let text: string = await response.text(); + try { + text = JSON.parse(text); + } catch {} return { - data: text, + data: text as any, headers: Object.fromEntries(response.headers.entries()), status: response.status, }; From af9ac0b9e966d40ca6b4756600c3d2dcaa33ad66 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:59:12 +0100 Subject: [PATCH 12/20] 2.3.0-alpha.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04c8537..dce3667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xapi/xapi", - "version": "2.2.5", + "version": "2.3.0-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@xapi/xapi", - "version": "2.2.5", + "version": "2.3.0-alpha.0", "license": "MIT", "dependencies": { "axios": "^1.6.0" diff --git a/package.json b/package.json index 6928070..c07b33d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xapi/xapi", - "version": "2.2.5", + "version": "2.3.0-alpha.0", "description": "Communicate over xAPI using JavaScript.", "main": "dist/XAPI.cjs.js", "module": "dist/XAPI.esm.js", From 511eda1f35013dbfda8321fca6d2436ef344f266 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:03:15 +0100 Subject: [PATCH 13/20] fix: Improve btoa detection --- src/helpers/toBasicAuth/toBasicAuth.ts | 2 +- src/helpers/toBasicAuth/toBasicAuth.unit.test.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/helpers/toBasicAuth/toBasicAuth.ts b/src/helpers/toBasicAuth/toBasicAuth.ts index 7784906..4c40e49 100644 --- a/src/helpers/toBasicAuth/toBasicAuth.ts +++ b/src/helpers/toBasicAuth/toBasicAuth.ts @@ -1,6 +1,6 @@ export function toBasicAuth(username: string, password: string): string { const credentials = `${username}:${password}`; - if (typeof window !== "undefined" && window.btoa) { + if (typeof btoa === "function") { return `Basic ${btoa(credentials)}`; } else if (typeof Buffer !== "undefined") { return `Basic ${Buffer.from(credentials, "binary").toString("base64")}`; diff --git a/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts b/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts index 81e2e3d..a9b5159 100644 --- a/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts +++ b/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts @@ -23,8 +23,7 @@ test("converts username and password into Basic Auth header", () => { }); test("throws error if environment not supported", () => { - if (typeof window !== "undefined") window.btoa = undefined; - // eslint-disable-next-line no-global-assign + if (typeof btoa === "function") btoa = undefined; if (Buffer) Buffer = undefined; expect(() => toBasicAuth("", "")).toThrowError(); }); From 15c4dd15f2e9ba32ddeb208fd5fcab4a528577bf Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:45:32 +0100 Subject: [PATCH 14/20] feat: Add edge jest environment with no coverage collection --- jest.config.edge.js | 19 ++++ jest.config.js | 22 +++- package-lock.json | 171 ++++++++++++++++++++++++++++ package.json | 4 +- test/mockAxios.ts | 11 ++ test/{setupUnit.ts => mockFetch.ts} | 12 -- 6 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 jest.config.edge.js create mode 100644 test/mockAxios.ts rename test/{setupUnit.ts => mockFetch.ts} (59%) diff --git a/jest.config.edge.js b/jest.config.edge.js new file mode 100644 index 0000000..6117ae9 --- /dev/null +++ b/jest.config.edge.js @@ -0,0 +1,19 @@ +require("dotenv").config(); + +module.exports = { + preset: "ts-jest", + projects: [ + { + displayName: "edge-fetch-unit", + testMatch: ["**/*.unit.test.ts"], + testEnvironment: "@edge-runtime/jest-environment", + setupFiles: ["./test/mockFetch.ts", "./test/setupFetch.ts"], + }, + { + displayName: "edge-fetch-int", + testMatch: ["**/*.int.test.ts"], + testEnvironment: "@edge-runtime/jest-environment", + setupFilesAfterEnv: ["./test/setupInt.ts", "./test/setupFetch.ts"], + }, + ], +}; diff --git a/jest.config.js b/jest.config.js index bceedb9..171a7a7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,15 +16,19 @@ module.exports = { displayName: "dom-axios-unit", testMatch: ["**/*.unit.test.ts"], testEnvironment: "jsdom", - setupFiles: ["./test/setupUnit.ts", "./test/setupAxios.ts"], + setupFiles: [ + "./test/mockAxios.ts", + "./test/mockFetch.ts", + "./test/setupAxios.ts", + ], }, { displayName: "dom-fetch-unit", testMatch: ["**/*.unit.test.ts"], testEnvironment: "jsdom", setupFiles: [ - "./test/setupUnit.ts", - "./test/polyfillFetch.ts", + "./test/mockAxios.ts", + "./test/mockFetch.ts", "./test/setupFetch.ts", ], }, @@ -32,13 +36,21 @@ module.exports = { displayName: "node-axios-unit", testMatch: ["**/*.unit.test.ts"], testEnvironment: "node", - setupFiles: ["./test/setupUnit.ts", "./test/setupAxios.ts"], + setupFiles: [ + "./test/mockAxios.ts", + "./test/mockFetch.ts", + "./test/setupAxios.ts", + ], }, { displayName: "node-fetch-unit", testMatch: ["**/*.unit.test.ts"], testEnvironment: "node", - setupFiles: ["./test/setupUnit.ts", "./test/setupFetch.ts"], + setupFiles: [ + "./test/mockAxios.ts", + "./test/mockFetch.ts", + "./test/setupFetch.ts", + ], }, { displayName: "dom-axios-int", diff --git a/package-lock.json b/package-lock.json index dce3667..27c8abd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/preset-env": "^7.23.2", "@babel/preset-typescript": "^7.23.2", + "@edge-runtime/jest-environment": "^2.3.10", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.0.1", @@ -1829,6 +1830,176 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@edge-runtime/jest-environment": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@edge-runtime/jest-environment/-/jest-environment-2.3.10.tgz", + "integrity": "sha512-wms2hveQV18DnWkiqzpjzIJ4SoD3+dXzuQ3/GAsklQDCGd4ClkREu5zCy0TFha0mR4I9tqi0DB8nD5Ldd/r1Pg==", + "dev": true, + "dependencies": { + "@edge-runtime/vm": "3.2.0", + "@jest/environment": "29.5.0", + "@jest/fake-timers": "29.5.0", + "jest-mock": "29.5.0", + "jest-util": "29.5.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@edge-runtime/jest-environment/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/@edge-runtime/jest-environment/node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edge-runtime/jest-environment/node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@edge-runtime/jest-environment/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/@edge-runtime/primitives": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/primitives/-/primitives-4.1.0.tgz", + "integrity": "sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@edge-runtime/vm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/vm/-/vm-3.2.0.tgz", + "integrity": "sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==", + "dev": true, + "dependencies": { + "@edge-runtime/primitives": "4.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", diff --git a/package.json b/package.json index c07b33d..c38cc47 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "build:types": "tsc --emitDeclarationOnly", "build": "npm run clean && npm run build:types && npm run build:js", "format": "prettier --write '**/*.{js,jsx,json,ts,tsx}'", - "test": "jest --runInBand && npm run test:example:node:require && npm run test:example:node:import", + "test": "jest --runInBand && npm run test:edge && npm run test:example:node:require && npm run test:example:node:import", + "test:edge": "jest --runInBand --config jest.config.edge.js", "test:unit": "jest --selectProjects dom-axios-unit dom-fetch-unit node-axios-unit node-fetch-unit", "test:int": "jest --selectProjects dom-axios-int dom-fetch-int node-axios-int node-fetch-int --runInBand", "test:format": "prettier --check .", @@ -43,6 +44,7 @@ "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/preset-env": "^7.23.2", "@babel/preset-typescript": "^7.23.2", + "@edge-runtime/jest-environment": "^2.3.10", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.0.1", diff --git a/test/mockAxios.ts b/test/mockAxios.ts new file mode 100644 index 0000000..3d35729 --- /dev/null +++ b/test/mockAxios.ts @@ -0,0 +1,11 @@ +import axios from "axios"; + +jest.mock("axios"); + +(axios.request as any).mockImplementation(() => + Promise.resolve({ + headers: { + "content-type": "application/json", + }, + }) +); diff --git a/test/setupUnit.ts b/test/mockFetch.ts similarity index 59% rename from test/setupUnit.ts rename to test/mockFetch.ts index 9f00110..7cbfc6c 100644 --- a/test/setupUnit.ts +++ b/test/mockFetch.ts @@ -1,15 +1,3 @@ -import axios from "axios"; - -jest.mock("axios"); - -(axios.request as any).mockImplementation(() => - Promise.resolve({ - headers: { - "content-type": "application/json", - }, - }) -); - global.fetch = jest.fn(() => Promise.resolve({ json: () => From 26ad15138b5da3f615a5680b138c78c4d6c4e1ec Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:46:29 +0100 Subject: [PATCH 15/20] test: Skip testing axios adapter in edge environment --- src/adapters/axiosAdapter.unit.test.ts | 3 ++- test/jestUtils.ts | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/adapters/axiosAdapter.unit.test.ts b/src/adapters/axiosAdapter.unit.test.ts index 8871cbf..6beb220 100644 --- a/src/adapters/axiosAdapter.unit.test.ts +++ b/src/adapters/axiosAdapter.unit.test.ts @@ -1,8 +1,9 @@ import axios from "axios"; import axiosAdapter from "./axiosAdapter"; +import { isEdgeRuntime, testIf } from "../../test/jestUtils"; describe("axiosAdapter", () => { - it("makes an axios network request", async () => { + testIf(!isEdgeRuntime())("makes an axios network request", async () => { axiosAdapter({ url: "https://www.example.com", method: "POST", diff --git a/test/jestUtils.ts b/test/jestUtils.ts index 4d9c764..8f4acb4 100644 --- a/test/jestUtils.ts +++ b/test/jestUtils.ts @@ -2,4 +2,7 @@ const testIf = (condition: boolean): jest.It => (condition ? test : test.skip); const isNode = (): boolean => typeof window === "undefined"; -export { testIf, isNode }; +// @ts-ignore +const isEdgeRuntime = (): boolean => typeof EdgeRuntime !== "undefined"; + +export { testIf, isNode, isEdgeRuntime }; From 8a2613a4db28300fbddd83a58a1570885ec1dc70 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:48:02 +0100 Subject: [PATCH 16/20] change: Replace uuid.v4 with built-in crypto.randomUUID --- package-lock.json | 14 -------------- package.json | 1 - .../createActivityProfile.int.test.ts | 4 ++-- .../deleteActivityProfile.int.test.ts | 4 ++-- .../state/createState/createState.int.test.ts | 4 ++-- .../state/deleteState/deleteState.int.test.ts | 4 ++-- .../state/deleteStates/deleteStates.int.test.ts | 4 ++-- .../document/state/getState/getState.int.test.ts | 4 ++-- .../document/state/getStates/getStates.int.test.ts | 4 ++-- .../document/state/setState/setState.int.test.ts | 4 ++-- 10 files changed, 16 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 27c8abd..40ca4d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "rollup": "^4.3.0", "ts-jest": "^29.1.1", "typescript": "^5.2.2", - "uuid": "^9.0.1", "whatwg-fetch": "^3.6.20" } }, @@ -9710,19 +9709,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-to-istanbul": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", diff --git a/package.json b/package.json index c38cc47..50dbb88 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "rollup": "^4.3.0", "ts-jest": "^29.1.1", "typescript": "^5.2.2", - "uuid": "^9.0.1", "whatwg-fetch": "^3.6.20" }, "dependencies": { diff --git a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.int.test.ts b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.int.test.ts index 4150f02..37df172 100644 --- a/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.int.test.ts +++ b/src/resources/document/activityProfile/createActivityProfile/createActivityProfile.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testActivity, testProfileId, @@ -21,7 +21,7 @@ forEachLRS((xapi) => { }); test("can add to an activity profile using an etag", () => { - const profileId = uuidv4(); + const profileId = crypto.randomUUID(); return xapi .createActivityProfile({ activityId: testActivity.id, diff --git a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.int.test.ts b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.int.test.ts index 6270746..bb1b486 100644 --- a/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.int.test.ts +++ b/src/resources/document/activityProfile/deleteActivityProfile/deleteActivityProfile.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testActivity, testDocument, @@ -20,7 +20,7 @@ forEachLRS((xapi) => { }); test("can delete an activity profile with an etag", () => { - const profileId = uuidv4(); + const profileId = crypto.randomUUID(); return xapi .createActivityProfile({ activityId: testActivity.id, diff --git a/src/resources/document/state/createState/createState.int.test.ts b/src/resources/document/state/createState/createState.int.test.ts index 43d7924..324610d 100644 --- a/src/resources/document/state/createState/createState.int.test.ts +++ b/src/resources/document/state/createState/createState.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testAgent, testActivity, @@ -30,7 +30,7 @@ forEachLRS((xapi) => { activityId: testActivity.id, stateId: testStateId, state: testDocument, - registration: uuidv4(), + registration: crypto.randomUUID(), }) .then((response) => { return expect(response.data).toBeDefined(); diff --git a/src/resources/document/state/deleteState/deleteState.int.test.ts b/src/resources/document/state/deleteState/deleteState.int.test.ts index 2dce3e8..e42dcab 100644 --- a/src/resources/document/state/deleteState/deleteState.int.test.ts +++ b/src/resources/document/state/deleteState/deleteState.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testAgent, testActivity, @@ -23,7 +23,7 @@ forEachLRS((xapi) => { }); test("can delete a state with a registration", () => { - const registration = uuidv4(); + const registration = crypto.randomUUID(); return xapi .createState({ agent: testAgent, diff --git a/src/resources/document/state/deleteStates/deleteStates.int.test.ts b/src/resources/document/state/deleteStates/deleteStates.int.test.ts index 7d3e25e..e8db11e 100644 --- a/src/resources/document/state/deleteStates/deleteStates.int.test.ts +++ b/src/resources/document/state/deleteStates/deleteStates.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testActivity, testAgent, @@ -22,7 +22,7 @@ forEachLRS((xapi) => { }); test("can delete all states for a registration", () => { - const registration = uuidv4(); + const registration = crypto.randomUUID(); return xapi .createState({ agent: testAgent, diff --git a/src/resources/document/state/getState/getState.int.test.ts b/src/resources/document/state/getState/getState.int.test.ts index 4f829b4..b0076a9 100644 --- a/src/resources/document/state/getState/getState.int.test.ts +++ b/src/resources/document/state/getState/getState.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testAgent, testActivity, @@ -31,7 +31,7 @@ forEachLRS((xapi) => { }); test("can get a state with a registration", () => { - const registration = uuidv4(); + const registration = crypto.randomUUID(); return xapi .createState({ agent: testAgent, diff --git a/src/resources/document/state/getStates/getStates.int.test.ts b/src/resources/document/state/getStates/getStates.int.test.ts index 25b100e..06941ed 100644 --- a/src/resources/document/state/getStates/getStates.int.test.ts +++ b/src/resources/document/state/getStates/getStates.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testAgent, testActivity, @@ -22,7 +22,7 @@ forEachLRS((xapi) => { }); test("can get all states with a registration", () => { - const registration = uuidv4(); + const registration = crypto.randomUUID(); const stateId = new Date().getTime().toString(); return xapi .createState({ diff --git a/src/resources/document/state/setState/setState.int.test.ts b/src/resources/document/state/setState/setState.int.test.ts index 55c4304..489ae27 100644 --- a/src/resources/document/state/setState/setState.int.test.ts +++ b/src/resources/document/state/setState/setState.int.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; import { testAgent, testActivity, @@ -31,7 +31,7 @@ forEachLRS((xapi) => { activityId: testActivity.id, stateId: testStateId, state: testDocument, - registration: uuidv4(), + registration: crypto.randomUUID(), }) .then((result) => { return expect(result.data).toBeDefined(); From 1e1f72e130403ba2bc19d246d35b2c708ecd1440 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:01:34 +0100 Subject: [PATCH 17/20] 2.3.0-alpha.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40ca4d7..397d8e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xapi/xapi", - "version": "2.3.0-alpha.0", + "version": "2.3.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@xapi/xapi", - "version": "2.3.0-alpha.0", + "version": "2.3.0-alpha.1", "license": "MIT", "dependencies": { "axios": "^1.6.0" diff --git a/package.json b/package.json index 50dbb88..d68de25 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xapi/xapi", - "version": "2.3.0-alpha.0", + "version": "2.3.0-alpha.1", "description": "Communicate over xAPI using JavaScript.", "main": "dist/XAPI.cjs.js", "module": "dist/XAPI.esm.js", From 15f7b4a40f2a0d00f941f7309ac3d711f88db870 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:32:42 +0100 Subject: [PATCH 18/20] chore: Fix linter errors --- src/adapters/fetchAdapter.ts | 1 + src/helpers/toBasicAuth/toBasicAuth.unit.test.ts | 8 +++++++- test/jestUtils.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/adapters/fetchAdapter.ts b/src/adapters/fetchAdapter.ts index 77dac40..44d3f35 100644 --- a/src/adapters/fetchAdapter.ts +++ b/src/adapters/fetchAdapter.ts @@ -23,6 +23,7 @@ const fetchAdapter: AdapterFunction = async ({ let text: string = await response.text(); try { text = JSON.parse(text); + // eslint-disable-next-line no-empty } catch {} return { data: text as any, diff --git a/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts b/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts index a9b5159..4a3293a 100644 --- a/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts +++ b/src/helpers/toBasicAuth/toBasicAuth.unit.test.ts @@ -23,7 +23,13 @@ test("converts username and password into Basic Auth header", () => { }); test("throws error if environment not supported", () => { + // @ts-expect-error Overriding global/window btoa + // eslint-disable-next-line no-global-assign if (typeof btoa === "function") btoa = undefined; + // @ts-expect-error Overriding global/window Buffer + // eslint-disable-next-line no-global-assign if (Buffer) Buffer = undefined; - expect(() => toBasicAuth("", "")).toThrowError(); + expect(() => toBasicAuth("", "")).toThrow( + new Error("Environment does not support base64 conversion.") + ); }); diff --git a/test/jestUtils.ts b/test/jestUtils.ts index 8f4acb4..455ad4e 100644 --- a/test/jestUtils.ts +++ b/test/jestUtils.ts @@ -2,7 +2,7 @@ const testIf = (condition: boolean): jest.It => (condition ? test : test.skip); const isNode = (): boolean => typeof window === "undefined"; -// @ts-ignore +// @ts-expect-error EdgeRuntime is not present in local environment const isEdgeRuntime = (): boolean => typeof EdgeRuntime !== "undefined"; export { testIf, isNode, isEdgeRuntime }; From 5135b7625dcc5eb1d392a0556ae1d03f5da47416 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:32:21 +0100 Subject: [PATCH 19/20] fix: Patch axios version --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6346a58..98934d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4056,9 +4056,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", From ada7c893634ec90d1184f87b0980aab175e13bd0 Mon Sep 17 00:00:00 2001 From: Christian Cook <3473396+CookieCookson@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:33:42 +0100 Subject: [PATCH 20/20] 3.0.0 --- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a80ed46..de877c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 3.0.0 (10 Sep 2024) + +- Added `adapter` property to `XAPI` constructor params, allowing a choice between `axios` (Default), `fetch` or a custom adapter function +- Adds support for edge environments through the `fetch` adapter +- Updated `axios` to patch security vulnerability ([CVE-2024-39338](https://github.com/advisories/GHSA-8hc4-vh64-cxmj)) + # 2.2.8 (31 Jul 2024) - Updated `braces` to patch security vulnerability diff --git a/package-lock.json b/package-lock.json index 98934d1..2eb3842 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xapi/xapi", - "version": "2.3.0-alpha.1", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@xapi/xapi", - "version": "2.3.0-alpha.1", + "version": "3.0.0", "license": "MIT", "dependencies": { "axios": "^1.6.0" diff --git a/package.json b/package.json index 01888e8..9ca3d7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xapi/xapi", - "version": "2.3.0-alpha.1", + "version": "3.0.0", "description": "Communicate over xAPI using JavaScript.", "main": "dist/XAPI.cjs.js", "module": "dist/XAPI.esm.js",