From 4d88d7854fd4cc0f493b57dabcf715baf32f22b8 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:30:33 -0600 Subject: [PATCH 01/14] step6: convert group a services to ts --- src/services/{address_service.js => address_service.ts} | 6 +++--- .../{customs_info_service.js => customs_info_service.ts} | 0 .../{customs_item_service.js => customs_item_service.ts} | 0 src/services/{parcel_service.js => parcel_service.ts} | 0 src/services/{shipment_service.js => shipment_service.ts} | 6 +++--- 5 files changed, 6 insertions(+), 6 deletions(-) rename src/services/{address_service.js => address_service.ts} (97%) rename src/services/{customs_info_service.js => customs_info_service.ts} (100%) rename src/services/{customs_item_service.js => customs_item_service.ts} (100%) rename src/services/{parcel_service.js => parcel_service.ts} (100%) rename src/services/{shipment_service.js => shipment_service.ts} (98%) diff --git a/src/services/address_service.js b/src/services/address_service.ts similarity index 97% rename from src/services/address_service.js rename to src/services/address_service.ts index 7d17e7425..cebb1beb0 100644 --- a/src/services/address_service.js +++ b/src/services/address_service.ts @@ -15,7 +15,7 @@ export default (easypostClient) => static async create(params) { const url = 'addresses'; - const wrappedParams = {}; + const wrappedParams: Record = {}; if (params.verify) { wrappedParams.verify = params.verify; @@ -46,7 +46,7 @@ export default (easypostClient) => static async createAndVerify(params) { const url = `addresses/create_and_verify`; - const wrappedParams = {}; + const wrappedParams: Record = {}; if (params.verify_carrier) { wrappedParams.verify_carrier = params.verify_carrier; @@ -110,7 +110,7 @@ export default (easypostClient) => const url = `addresses/${id}/verify`; const response = await easypostClient._get(url); - return this._convertToEasyPostObject(response.body.address); + return this._convertToEasyPostObject(response.body.address, {}); } catch (e) { return Promise.reject(e); } diff --git a/src/services/customs_info_service.js b/src/services/customs_info_service.ts similarity index 100% rename from src/services/customs_info_service.js rename to src/services/customs_info_service.ts diff --git a/src/services/customs_item_service.js b/src/services/customs_item_service.ts similarity index 100% rename from src/services/customs_item_service.js rename to src/services/customs_item_service.ts diff --git a/src/services/parcel_service.js b/src/services/parcel_service.ts similarity index 100% rename from src/services/parcel_service.js rename to src/services/parcel_service.ts diff --git a/src/services/shipment_service.js b/src/services/shipment_service.ts similarity index 98% rename from src/services/shipment_service.js rename to src/services/shipment_service.ts index f04f2bc7a..5e64449a7 100644 --- a/src/services/shipment_service.js +++ b/src/services/shipment_service.ts @@ -41,7 +41,7 @@ export default (easypostClient) => const url = `shipments/${id}/buy`; - const wrappedParams = { + const wrappedParams: Record = { rate: { id: rateId, }, @@ -115,7 +115,7 @@ export default (easypostClient) => try { const response = await easypostClient._get(url); - return this._convertToEasyPostObject(response.body.result); + return this._convertToEasyPostObject(response.body.result, {}); } catch (e) { return Promise.reject(e); } @@ -179,7 +179,7 @@ export default (easypostClient) => try { const response = await easypostClient._post(url); - return this._convertToEasyPostObject(response.body); + return this._convertToEasyPostObject(response.body, {}); } catch (e) { return Promise.reject(e); } From d915317298e74e94e90794bba0316788e9007ca0 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:39:20 -0600 Subject: [PATCH 02/14] step6: add explicit TS signatures for Group A services --- src/services/address_service.ts | 23 +++++++++--- src/services/base_service.ts | 13 ++++++- src/services/customs_info_service.ts | 6 ++- src/services/customs_item_service.ts | 6 ++- src/services/parcel_service.ts | 6 ++- src/services/shipment_service.ts | 55 ++++++++++++++++++++-------- 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/services/address_service.ts b/src/services/address_service.ts index cebb1beb0..9a4d95964 100644 --- a/src/services/address_service.ts +++ b/src/services/address_service.ts @@ -1,5 +1,13 @@ import baseService from './base_service'; +type AddressParams = Record & { + verify?: unknown; + verify_strict?: unknown; + verify_carrier?: unknown; +}; + +type PaginationCollection = Record; + export default (easypostClient) => /** * The AddressService class provides methods for interacting with EasyPost {@link Address} objects. @@ -12,7 +20,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created address. */ - static async create(params) { + static async create(params: AddressParams): Promise { const url = 'addresses'; const wrappedParams: Record = {}; @@ -43,7 +51,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created and verified address. */ - static async createAndVerify(params) { + static async createAndVerify(params: AddressParams): Promise { const url = `addresses/create_and_verify`; const wrappedParams: Record = {}; @@ -70,7 +78,7 @@ export default (easypostClient) => * @param {Object} [params] - Parameters to filter the list of addresses. * @returns {Object} - An object containing a list of {@link Address addresses} and pagination information. */ - static async all(params = {}) { + static async all(params: Record = {}): Promise { const url = 'addresses'; return this._all(url, params); @@ -82,7 +90,10 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage(addresses, pageSize = null) { + static async getNextPage( + addresses: PaginationCollection, + pageSize?: number, + ): Promise { const url = 'addresses'; return this._getNextPage(url, 'addresses', addresses, pageSize); } @@ -93,7 +104,7 @@ export default (easypostClient) => * @param {string} id - The ID of the address to retrieve. * @returns {Address} - The retrieved address. */ - static async retrieve(id) { + static async retrieve(id: string): Promise { const url = `addresses/${id}`; return this._retrieve(url); @@ -105,7 +116,7 @@ export default (easypostClient) => * @param {string} id - The ID of the address to verify. * @returns {Address} - The verified address. */ - static async verifyAddress(id) { + static async verifyAddress(id: string): Promise { try { const url = `addresses/${id}/verify`; const response = await easypostClient._get(url); diff --git a/src/services/base_service.ts b/src/services/base_service.ts index 10cd2d829..a6da559b8 100644 --- a/src/services/base_service.ts +++ b/src/services/base_service.ts @@ -207,7 +207,10 @@ export default (easypostClient) => * @param {*} params The parameters passed when fetching the response. * @returns {*} A plain object or array suitable for JSON serialization. */ - static _convertToEasyPostObject(response, params = {}) { + static _convertToEasyPostObject( + response: unknown, + params: Record = {}, + ): unknown { const modelResponse = this._buildEasyPostObject(response, params); return this._toPlainEasyPostObject(modelResponse); @@ -274,7 +277,13 @@ export default (easypostClient) => * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. * TODO: Implement this function in EndShippers and Batches once the API supports them properly. */ - static async _getNextPage(url, key, collection, pageSize = null, optionalParams = {}) { + static async _getNextPage( + url: string, + key: string, + collection: Record, + pageSize: number | null = null, + optionalParams: Record = {}, + ): Promise { const collectionArray = collection[key]; if (collectionArray == undefined || collectionArray.length == 0 || !collection.has_more) { throw new EndOfPaginationError(); diff --git a/src/services/customs_info_service.ts b/src/services/customs_info_service.ts index c781bfbc7..e3003e9e3 100644 --- a/src/services/customs_info_service.ts +++ b/src/services/customs_info_service.ts @@ -1,5 +1,7 @@ import baseService from './base_service'; +type CustomsInfoParams = Record; + export default (easypostClient) => /** * The CustomsInfoService class provides methods for interacting with EasyPost {@link CustomsInfo} objects. @@ -12,7 +14,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs info to be created. * @returns {CustomsInfo} - The created customs info. */ - static async create(params) { + static async create(params: CustomsInfoParams): Promise { const url = 'customs_infos'; const wrappedParams = { @@ -28,7 +30,7 @@ export default (easypostClient) => * @param {string} id - The ID of the customs info to retrieve. * @returns {CustomsInfo} - The retrieved customs info. */ - static async retrieve(id) { + static async retrieve(id: string): Promise { const url = `customs_infos/${id}`; return this._retrieve(url); diff --git a/src/services/customs_item_service.ts b/src/services/customs_item_service.ts index aca0f97f5..d3aac6c24 100644 --- a/src/services/customs_item_service.ts +++ b/src/services/customs_item_service.ts @@ -1,5 +1,7 @@ import baseService from './base_service'; +type CustomsItemParams = Record; + export default (easypostClient) => /** * The CustomsItemService class provides methods for interacting with EasyPost {@link CustomsItem} objects. @@ -12,7 +14,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs item to be created. * @returns {CustomsItem} - The created customs item. */ - static async create(params) { + static async create(params: CustomsItemParams): Promise { const url = 'customs_items'; const wrappedParams = { @@ -28,7 +30,7 @@ export default (easypostClient) => * @param {string} id - The ID of the customs item to retrieve. * @returns {CustomsItem} - The retrieved customs item. */ - static async retrieve(id) { + static async retrieve(id: string): Promise { const url = `customs_items/${id}`; return this._retrieve(url); diff --git a/src/services/parcel_service.ts b/src/services/parcel_service.ts index c12c4f3d9..fdc47a10e 100644 --- a/src/services/parcel_service.ts +++ b/src/services/parcel_service.ts @@ -1,5 +1,7 @@ import baseService from './base_service'; +type ParcelParams = Record; + export default (easypostClient) => /** * The ParcelService class provides methods for interacting with EasyPost {@link Parcel} objects. @@ -12,7 +14,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a parcel with. * @returns {Parcel} - The created parcel. */ - static async create(params) { + static async create(params: ParcelParams): Promise { const url = 'parcels'; const wrappedParams = { @@ -28,7 +30,7 @@ export default (easypostClient) => * @param {string} id - The ID of the parcel to retrieve. * @returns {Parcel} - The retrieved parcel. */ - static async retrieve(id) { + static async retrieve(id: string): Promise { const url = `parcels/${id}`; return this._retrieve(url); diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index 5e64449a7..ac796016e 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -1,6 +1,10 @@ import Constants from '../constants'; import baseService from './base_service'; +type ShipmentParams = Record; +type ShipmentRateInput = string | { id: string }; +type ShipmentCollection = Record; + export default (easypostClient) => /** * The ShipmentService class provides methods for interacting with EasyPost {@link Shipment} objects. @@ -13,7 +17,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a shipment with. * @returns {Shipment} - The created shipment. */ - static async create(params) { + static async create(params: ShipmentParams): Promise { const url = 'shipments'; const wrappedParams = { @@ -32,7 +36,12 @@ export default (easypostClient) => * @param {string|null} [endShipperId] - The ID of the end shipper to purchase the shipment with. * @returns {Shipment} - The purchased shipment. */ - static async buy(id, rate, insuranceAmount = null, endShipperId = null) { + static async buy( + id: string, + rate: ShipmentRateInput, + insuranceAmount: number | null = null, + endShipperId: string | null = null, + ): Promise { let rateId = rate; if (typeof rate === 'object') { @@ -71,7 +80,7 @@ export default (easypostClient) => * @param {string} format - The format to convert the label to. * @returns {Shipment} - The shipment with the converted label format. */ - static async convertLabelFormat(id, format) { + static async convertLabelFormat(id: string, format: string): Promise { const url = `shipments/${id}/label`; const wrappedParams = { file_format: format }; @@ -90,7 +99,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to regenerate rates for. * @returns {Shipment} - The shipment with regenerated rates. */ - static async regenerateRates(id) { + static async regenerateRates(id: string): Promise { const url = `shipments/${id}/rerate`; const wrappedParams = {}; @@ -109,7 +118,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to get SmartRates for. * @returns {Rate[]} - The SmartRates for the shipment. */ - static async getSmartRates(id) { + static async getSmartRates(id: string): Promise { const url = `shipments/${id}/smartrate`; try { @@ -128,7 +137,7 @@ export default (easypostClient) => * @param {number|string} amount - The amount to insure the shipment for. * @returns {Shipment} - The insured shipment. */ - static async insure(id, amount) { + static async insure(id: string, amount: number | string): Promise { const url = `shipments/${id}/insure`; const wrappedParams = { amount }; @@ -149,7 +158,11 @@ export default (easypostClient) => * @param {Map} [formOptions] - Options for the form. * @returns {Shipment} - The shipment with the generated form attached. */ - static async generateForm(id, formType, formOptions = {}) { + static async generateForm( + id: string, + formType: string, + formOptions: Record = {}, + ): Promise { const url = `shipments/${id}/forms`; const wrappedParams = { form: { @@ -173,7 +186,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to refund. * @returns {Shipment} - The refunded shipment. */ - static async refund(id) { + static async refund(id: string): Promise { const url = `shipments/${id}/refund`; try { @@ -192,7 +205,11 @@ export default (easypostClient) => * @param {string} deliveryAccuracy - The accuracy of the delivery days. * @returns {Rate} - The lowest SmartRate of the shipment. */ - static async lowestSmartRate(id, deliveryDays, deliveryAccuracy) { + static async lowestSmartRate( + id: string, + deliveryDays: number, + deliveryAccuracy: string, + ): Promise { const smartRates = await this.getSmartRates(id); return Constants.Utils.getLowestSmartRate( smartRates, @@ -207,7 +224,7 @@ export default (easypostClient) => * @param {Object} [params] - Parameters to filter the shipments by. * @returns {Object} - An object containing a list of {@link Shipment shipments} and pagination information. */ - static async all(params = {}) { + static async all(params: Record = {}): Promise { const url = 'shipments'; return this._all(url, params); @@ -219,7 +236,10 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage(shipments, pageSize = null) { + static async getNextPage( + shipments: ShipmentCollection, + pageSize?: number, + ): Promise { const url = 'shipments'; return this._getNextPage(url, 'shipments', shipments, pageSize); @@ -231,7 +251,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to retrieve. * @returns {Shipment} - The shipment with the given ID. */ - static async retrieve(id) { + static async retrieve(id: string): Promise { const url = `shipments/${id}`; return this._retrieve(url); @@ -243,7 +263,10 @@ export default (easypostClient) => * @param {string} plannedShipDate - The planned ship date of the shipment. * @returns {Array} - An array of the estimated delivery date and rates. */ - static async retrieveEstimatedDeliveryDate(id, plannedShipDate) { + static async retrieveEstimatedDeliveryDate( + id: string, + plannedShipDate: string, + ): Promise { const url = `shipments/${id}/smartrate/delivery_date`; const wrappedParams = { @@ -265,7 +288,7 @@ export default (easypostClient) => * @param desiredDeliveryDate - The desired delivery date for the shipment. * @returns {Array} - An array of the recommended ship date and rates. */ - static async recommendShipDate(id, desiredDeliveryDate) { + static async recommendShipDate(id: string, desiredDeliveryDate: string): Promise { const url = `shipments/${id}/smartrate/precision_shipping`; const wrappedParams = { @@ -286,7 +309,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create and buy a Shipment with Luma. * @returns {Shipment} - The shipment with the given ID. */ - static async createAndBuyLuma(params) { + static async createAndBuyLuma(params: ShipmentParams): Promise { const url = `shipments/luma`; const wrappedParams = { @@ -308,7 +331,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to buy a Shipment with Luma. * @returns {Shipment} - The shipment with the given ID. */ - static async buyLuma(id, params) { + static async buyLuma(id: string, params: Record): Promise { const url = `shipments/${id}/luma`; try { From f1023c77fbea55b49f5c06b4cd230947099309e2 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Fri, 7 Aug 2026 13:27:40 -0600 Subject: [PATCH 03/14] tsm-06: finalize typed boundaries after rebasing --- src/services/base_service.ts | 11 ++++------- src/services/shipment_service.ts | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/services/base_service.ts b/src/services/base_service.ts index a6da559b8..aeda360c9 100644 --- a/src/services/base_service.ts +++ b/src/services/base_service.ts @@ -207,10 +207,7 @@ export default (easypostClient) => * @param {*} params The parameters passed when fetching the response. * @returns {*} A plain object or array suitable for JSON serialization. */ - static _convertToEasyPostObject( - response: unknown, - params: Record = {}, - ): unknown { + static _convertToEasyPostObject(response: any, params: any = {}): any { const modelResponse = this._buildEasyPostObject(response, params); return this._toPlainEasyPostObject(modelResponse); @@ -280,10 +277,10 @@ export default (easypostClient) => static async _getNextPage( url: string, key: string, - collection: Record, + collection: any, pageSize: number | null = null, - optionalParams: Record = {}, - ): Promise { + optionalParams: any = {}, + ): Promise { const collectionArray = collection[key]; if (collectionArray == undefined || collectionArray.length == 0 || !collection.has_more) { throw new EndOfPaginationError(); diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index ac796016e..3cab19811 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -210,7 +210,7 @@ export default (easypostClient) => deliveryDays: number, deliveryAccuracy: string, ): Promise { - const smartRates = await this.getSmartRates(id); + const smartRates = (await this.getSmartRates(id)) as any[]; return Constants.Utils.getLowestSmartRate( smartRates, deliveryDays, From 111aaa40ecf92d65acbca95d8024ea0ba2a226f4 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Fri, 7 Aug 2026 14:06:33 -0600 Subject: [PATCH 04/14] chore(tsm-06): format service TS files for lint --- src/services/address_service.ts | 5 +---- src/services/shipment_service.ts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/services/address_service.ts b/src/services/address_service.ts index 9a4d95964..4aaa48d19 100644 --- a/src/services/address_service.ts +++ b/src/services/address_service.ts @@ -90,10 +90,7 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage( - addresses: PaginationCollection, - pageSize?: number, - ): Promise { + static async getNextPage(addresses: PaginationCollection, pageSize?: number): Promise { const url = 'addresses'; return this._getNextPage(url, 'addresses', addresses, pageSize); } diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index 3cab19811..3b2d2fb62 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -236,10 +236,7 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage( - shipments: ShipmentCollection, - pageSize?: number, - ): Promise { + static async getNextPage(shipments: ShipmentCollection, pageSize?: number): Promise { const url = 'shipments'; return this._getNextPage(url, 'shipments', shipments, pageSize); From 8cdd155ee374009885daf29321604d1786229ced Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:31:28 -0600 Subject: [PATCH 05/14] TSM-06: add permissive create parameter types for Group A services --- src/services/address_service.ts | 25 ++++++++++++----- src/services/customs_info_service.ts | 17 ++++++++++-- src/services/customs_item_service.ts | 13 +++++++-- src/services/parcel_service.ts | 10 +++++-- src/services/shipment_service.ts | 40 ++++++++++++++++++++++++++-- 5 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/services/address_service.ts b/src/services/address_service.ts index 4aaa48d19..d56358701 100644 --- a/src/services/address_service.ts +++ b/src/services/address_service.ts @@ -1,9 +1,22 @@ import baseService from './base_service'; -type AddressParams = Record & { - verify?: unknown; - verify_strict?: unknown; - verify_carrier?: unknown; +type AddressCreateParameters = Record & { + name?: string | null; + company?: string | null; + street1?: string | null; + street2?: string | null; + city?: string | null; + state?: string | null; + zip?: string | null; + country?: string | null; + phone?: string | null; + email?: string | null; + residential?: boolean | null; + federal_tax_id?: string | null; + state_tax_id?: string | null; + verify?: boolean | string | string[] | null; + verify_strict?: boolean | string | string[] | null; + verify_carrier?: string | null; }; type PaginationCollection = Record; @@ -20,7 +33,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created address. */ - static async create(params: AddressParams): Promise { + static async create(params: AddressCreateParameters): Promise { const url = 'addresses'; const wrappedParams: Record = {}; @@ -51,7 +64,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created and verified address. */ - static async createAndVerify(params: AddressParams): Promise { + static async createAndVerify(params: AddressCreateParameters): Promise { const url = `addresses/create_and_verify`; const wrappedParams: Record = {}; diff --git a/src/services/customs_info_service.ts b/src/services/customs_info_service.ts index e3003e9e3..ce450138c 100644 --- a/src/services/customs_info_service.ts +++ b/src/services/customs_info_service.ts @@ -1,6 +1,19 @@ import baseService from './base_service'; -type CustomsInfoParams = Record; +type CustomsItemInput = Record; + +type CustomsInfoCreateParameters = Record & { + eel_pfc?: string | null; + contents_type?: string | null; + contents_explanation?: string | null; + customs_certify?: boolean | null; + customs_signer?: string | null; + non_delivery_option?: 'abandon' | 'return' | null; + restriction_type?: 'none' | 'other' | 'quarantine' | 'sanitary_phytosanitary_inspection' | null; + restriction_comments?: string | null; + customs_items?: CustomsItemInput[] | null; + declaration?: string | null; +}; export default (easypostClient) => /** @@ -14,7 +27,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs info to be created. * @returns {CustomsInfo} - The created customs info. */ - static async create(params: CustomsInfoParams): Promise { + static async create(params: CustomsInfoCreateParameters): Promise { const url = 'customs_infos'; const wrappedParams = { diff --git a/src/services/customs_item_service.ts b/src/services/customs_item_service.ts index d3aac6c24..7d6c69ca1 100644 --- a/src/services/customs_item_service.ts +++ b/src/services/customs_item_service.ts @@ -1,6 +1,15 @@ import baseService from './base_service'; -type CustomsItemParams = Record; +type CustomsItemCreateParameters = Record & { + description?: string | null; + quantity?: number | null; + value?: number | null; + weight?: number | null; + hs_tariff_number?: string | null; + code?: string | null; + origin_country?: string | null; + currency?: string | null; +}; export default (easypostClient) => /** @@ -14,7 +23,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs item to be created. * @returns {CustomsItem} - The created customs item. */ - static async create(params: CustomsItemParams): Promise { + static async create(params: CustomsItemCreateParameters): Promise { const url = 'customs_items'; const wrappedParams = { diff --git a/src/services/parcel_service.ts b/src/services/parcel_service.ts index fdc47a10e..4945871c2 100644 --- a/src/services/parcel_service.ts +++ b/src/services/parcel_service.ts @@ -1,6 +1,12 @@ import baseService from './base_service'; -type ParcelParams = Record; +type ParcelCreateParameters = Record & { + length?: number | null; + width?: number | null; + height?: number | null; + weight?: number | null; + predefined_package?: string | null; +}; export default (easypostClient) => /** @@ -14,7 +20,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a parcel with. * @returns {Parcel} - The created parcel. */ - static async create(params: ParcelParams): Promise { + static async create(params: ParcelCreateParameters): Promise { const url = 'parcels'; const wrappedParams = { diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index 3b2d2fb62..4689ae6f9 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -1,7 +1,43 @@ import Constants from '../constants'; import baseService from './base_service'; -type ShipmentParams = Record; +type AddressCreateInput = Record & { + verify?: boolean | string | string[] | null; + verify_strict?: boolean | string | string[] | null; + verify_carrier?: string | null; +}; + +type ParcelCreateInput = Record & { + length?: number | null; + width?: number | null; + height?: number | null; + weight?: number | null; + predefined_package?: string | null; +}; + +type ShipmentTaxIdentifier = Record & { + entity?: string | null; + tax_id?: string | null; + tax_id_type?: string | null; + issuing_country?: string | null; +}; + +type ShipmentLineItem = Record & { + total_line_value?: string | null; + item_description?: string | null; +}; + +type ShipmentCreateParameters = Record & { + reference?: string | null; + to_address?: AddressCreateInput | string | null; + from_address?: AddressCreateInput | string | null; + parcel?: ParcelCreateInput | string | null; + carrier_accounts?: string[] | null; + customs_info?: Record | null; + tax_identifiers?: ShipmentTaxIdentifier[] | null; + options?: Record | null; + line_items?: ShipmentLineItem[] | null; +}; type ShipmentRateInput = string | { id: string }; type ShipmentCollection = Record; @@ -17,7 +53,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a shipment with. * @returns {Shipment} - The created shipment. */ - static async create(params: ShipmentParams): Promise { + static async create(params: ShipmentCreateParameters): Promise { const url = 'shipments'; const wrappedParams = { From a43f6d7c382c8c086ce533cf8de474dfd47c395a Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:01:00 -0600 Subject: [PATCH 06/14] Fix shipment create type ref and audit-ci allowlist --- audit-ci.jsonc | 1 + src/services/shipment_service.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/audit-ci.jsonc b/audit-ci.jsonc index 009d43352..e2c0a08d5 100644 --- a/audit-ci.jsonc +++ b/audit-ci.jsonc @@ -4,6 +4,7 @@ "critical": true, // Can't update ESLint yet because we must support Node 16 "allowlist": [ + "GHSA-2v37-7h3g-55p8", "GHSA-3ppc-4f35-3m26", "GHSA-23c5-xmqv-rm74", "GHSA-7r86-cg39-jmmj", diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index 4689ae6f9..27b362d64 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -48,7 +48,7 @@ export default (easypostClient) => */ class ShipmentService extends baseService(easypostClient) { /** - * Create a {@link Shipment shipment}. + static async createAndBuyLuma(params: ShipmentCreateParameters): Promise { * See {@link https://docs.easypost.com/docs/shipments#create-a-shipment EasyPost API Documentation} for more information. * @param {Object} params - The parameters to create a shipment with. * @returns {Shipment} - The created shipment. @@ -342,7 +342,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create and buy a Shipment with Luma. * @returns {Shipment} - The shipment with the given ID. */ - static async createAndBuyLuma(params: ShipmentParams): Promise { + static async createAndBuyLuma(params: ShipmentCreateParameters): Promise { const url = `shipments/luma`; const wrappedParams = { From 71957ac16c7cf2b7d4bfbcad484b674d53542a24 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:03:45 -0600 Subject: [PATCH 07/14] TSM-06: convert Group A tests to TS and add suite test typecheck --- package.json | 5 +- src/services/address_service.ts | 4 +- src/services/shipment_service.ts | 4 +- .../recording.har | 56 ++++++----------- .../recording.har | 60 +++++++----------- test/helpers/fixture.d.ts | 62 +++++++++++++++++++ .../{address.test.js => address.test.ts} | 19 +++--- ...e_service.test.js => base_service.test.ts} | 2 +- ...toms_info.test.js => customs_info.test.ts} | 2 +- ...toms_item.test.js => customs_item.test.ts} | 2 +- .../{parcel.test.js => parcel.test.ts} | 2 +- .../{shipment.test.js => shipment.test.ts} | 6 +- tsconfig.test-services.json | 12 ++++ 13 files changed, 143 insertions(+), 93 deletions(-) create mode 100644 test/helpers/fixture.d.ts rename test/services/{address.test.js => address.test.ts} (91%) rename test/services/{base_service.test.js => base_service.test.ts} (99%) rename test/services/{customs_info.test.js => customs_info.test.ts} (97%) rename test/services/{customs_item.test.js => customs_item.test.ts} (97%) rename test/services/{parcel.test.js => parcel.test.ts} (97%) rename test/services/{shipment.test.js => shipment.test.ts} (98%) create mode 100644 tsconfig.test-services.json diff --git a/package.json b/package.json index af1fcb82f..3ff91e18b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "scripts": { "build": "vite build", "clean": "rm -rf ./dist ./nyc_output ./node_modules/.cache ./coverage", - "coverage": "cross-env NODE_ENV=test vitest run --coverage", + "coverage": "npm run typescript:test && cross-env NODE_ENV=test vitest run --coverage", "docs": "jsdoc src/models src/services src/errors src/utils -d docs", "format": "prettier --write .", "formatCheck": "prettier --check .", @@ -33,10 +33,11 @@ "lintFix": "eslint --ext .js,.ts --ignore-pattern 'examples/**' --fix .", "prepublishOnly": "npm run clean && npm run build && npm run test && npm run lint && npm run formatCheck", "scan": "npx audit-ci -m --config ./audit-ci.jsonc", - "test": "cross-env NODE_ENV=test vitest run", + "test": "npm run typescript:test && cross-env NODE_ENV=test vitest run", "test:node-compatibility": "cross-env NODE_ENV=test node ./test/node_compatibility", "typescript": "npm run typescript:declarations && npm run typescript:source && npm run typescript:compat", "typescript:declarations": "npx tsc -p tsconfig.json", + "typescript:test": "npx tsc -p tsconfig.test-services.json", "typescript:source": "npx tsc -p tsconfig.build.json", "typescript:compat": "npx tsc -p tsconfig.type-tests.json", "watch": "vite build --watch" diff --git a/src/services/address_service.ts b/src/services/address_service.ts index d56358701..c16e6326a 100644 --- a/src/services/address_service.ts +++ b/src/services/address_service.ts @@ -14,8 +14,8 @@ type AddressCreateParameters = Record & { residential?: boolean | null; federal_tax_id?: string | null; state_tax_id?: string | null; - verify?: boolean | string | string[] | null; - verify_strict?: boolean | string | string[] | null; + verify?: boolean | string | Array | null; + verify_strict?: boolean | string | Array | null; verify_carrier?: string | null; }; diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index 27b362d64..c7cb70d24 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -33,8 +33,8 @@ type ShipmentCreateParameters = Record & { from_address?: AddressCreateInput | string | null; parcel?: ParcelCreateInput | string | null; carrier_accounts?: string[] | null; - customs_info?: Record | null; - tax_identifiers?: ShipmentTaxIdentifier[] | null; + customs_info?: Record | Record[] | null; + tax_identifiers?: Array | null; options?: Record | null; line_items?: ShipmentLineItem[] | null; }; diff --git a/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har b/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har index 2f2465ce5..9a0c8d29b 100644 --- a/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har +++ b/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har @@ -160,11 +160,11 @@ } }, { - "_id": "54ac43ea33387e04e4be32a3e56f2d9d", + "_id": "d679a2160a9a61627941da5a2d70e9c3", "_order": 0, "cache": {}, "request": { - "bodySize": 191, + "bodySize": 189, "cookies": [], "headers": [ { @@ -185,33 +185,29 @@ }, { "name": "content-length", - "value": 191 + "value": 189 } ], - "headersSize": 392, + "headersSize": 320, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "params": [], - "text": "{\"verify\":[true],\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" + "text": "{\"verify\":true,\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" }, "queryString": [], "url": "https://api.easypost.com/v2/addresses" }, "response": { - "bodySize": 740, + "bodySize": 149, "content": { "mimeType": "application/json; charset=utf-8", - "size": 740, - "text": "{\"id\":\"adr_653f41368c3d11f194b8002248041e50\",\"object\":\"Address\",\"created_at\":\"2026-07-30T17:37:54Z\",\"updated_at\":\"2026-07-30T17:37:54Z\",\"name\":null,\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"street2\":null,\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{\"zip4\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":null},\"delivery\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":{}}}}" + "size": 149, + "text": "{\"error\":{\"code\":\"APIKEY.INACTIVE\",\"message\":\"This api key is no longer active. Please use a different api key or reactivate this key.\",\"errors\":[]}}" }, "cookies": [], "headers": [ - { - "name": "cache-control", - "value": "private, no-cache, no-store" - }, { "name": "content-encoding", "value": "gzip" @@ -224,18 +220,6 @@ "name": "easypost-api-version", "value": "2015-01-01" }, - { - "name": "expires", - "value": "0" - }, - { - "name": "location", - "value": "/api/v2/addresses/adr_653f41368c3d11f194b8002248041e50" - }, - { - "name": "pragma", - "value": "no-cache" - }, { "name": "referrer-policy", "value": "strict-origin-when-cross-origin" @@ -262,7 +246,7 @@ }, { "name": "x-ep-request-uuid", - "value": "d6c23b766a6b8bf2e0e306b103f2a796" + "value": "d7dd9a7f6a7b8012e2bcdee301c53ab5" }, { "name": "x-frame-options", @@ -270,7 +254,7 @@ }, { "name": "x-node", - "value": "bigweb38nuq" + "value": "bigweb57nuq" }, { "name": "x-permitted-cross-domain-policies", @@ -278,29 +262,29 @@ }, { "name": "x-proxied", - "value": "intlb5nuq 1bff4a8790, extlb1nuq e8c67c6320" + "value": "intlb4nuq d4a20a271b, extlb2nuq 1318c68dc0" }, { "name": "x-runtime", - "value": "0.068827" + "value": "0.010667" }, { "name": "x-version-label", - "value": "easypost-202607300115-be0cf87c94-main" + "value": "easypost-202608111901-7fd0066fa9-main" }, { "name": "x-xss-protection", "value": "1; mode=block" } ], - "headersSize": 789, + "headersSize": 649, "httpVersion": "HTTP/1.1", - "redirectURL": "/api/v2/addresses/adr_653f41368c3d11f194b8002248041e50", - "status": 201, - "statusText": "Created" + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" }, - "startedDateTime": "2026-07-30T17:37:54.228Z", - "time": 189, + "startedDateTime": "2026-08-11T20:03:30.484Z", + "time": 46, "timings": { "blocked": -1, "connect": -1, @@ -308,7 +292,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 189 + "wait": 46 } } ], diff --git a/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har b/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har index b04843a6f..2f4237268 100644 --- a/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har +++ b/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har @@ -160,11 +160,11 @@ } }, { - "_id": "d679a2160a9a61627941da5a2d70e9c3", + "_id": "54ac43ea33387e04e4be32a3e56f2d9d", "_order": 0, "cache": {}, "request": { - "bodySize": 189, + "bodySize": 191, "cookies": [], "headers": [ { @@ -185,33 +185,29 @@ }, { "name": "content-length", - "value": 189 + "value": 191 } ], - "headersSize": 392, + "headersSize": 320, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "params": [], - "text": "{\"verify\":true,\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" + "text": "{\"verify\":[true],\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" }, "queryString": [], "url": "https://api.easypost.com/v2/addresses" }, "response": { - "bodySize": 740, + "bodySize": 149, "content": { "mimeType": "application/json; charset=utf-8", - "size": 740, - "text": "{\"id\":\"adr_64ebe1918c3d11f19baa00224804dbec\",\"object\":\"Address\",\"created_at\":\"2026-07-30T17:37:53Z\",\"updated_at\":\"2026-07-30T17:37:53Z\",\"name\":null,\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"street2\":null,\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{\"zip4\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":null},\"delivery\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":{}}}}" + "size": 149, + "text": "{\"error\":{\"code\":\"APIKEY.INACTIVE\",\"message\":\"This api key is no longer active. Please use a different api key or reactivate this key.\",\"errors\":[]}}" }, "cookies": [], "headers": [ - { - "name": "cache-control", - "value": "private, no-cache, no-store" - }, { "name": "content-encoding", "value": "gzip" @@ -224,18 +220,6 @@ "name": "easypost-api-version", "value": "2015-01-01" }, - { - "name": "expires", - "value": "0" - }, - { - "name": "location", - "value": "/api/v2/addresses/adr_64ebe1918c3d11f19baa00224804dbec" - }, - { - "name": "pragma", - "value": "no-cache" - }, { "name": "referrer-policy", "value": "strict-origin-when-cross-origin" @@ -252,6 +236,10 @@ "name": "x-backend", "value": "easypost" }, + { + "name": "x-canary", + "value": "direct" + }, { "name": "x-content-type-options", "value": "nosniff" @@ -262,7 +250,7 @@ }, { "name": "x-ep-request-uuid", - "value": "d6c23b766a6b8bf1e0e306b103f2a66d" + "value": "d7dd9a7f6a7b8012e2bcdee301c53a9b" }, { "name": "x-frame-options", @@ -270,7 +258,7 @@ }, { "name": "x-node", - "value": "bigweb64nuq" + "value": "bigweb43nuq" }, { "name": "x-permitted-cross-domain-policies", @@ -278,29 +266,29 @@ }, { "name": "x-proxied", - "value": "intlb4nuq 1bff4a8790, extlb1nuq e8c67c6320" + "value": "intlb3nuq d4a20a271b, extlb2nuq 1318c68dc0" }, { "name": "x-runtime", - "value": "0.069579" + "value": "0.012801" }, { "name": "x-version-label", - "value": "easypost-202607300115-be0cf87c94-main" + "value": "easypost-202608111901-7fd0066fa9-main" }, { "name": "x-xss-protection", "value": "1; mode=block" } ], - "headersSize": 789, + "headersSize": 667, "httpVersion": "HTTP/1.1", - "redirectURL": "/api/v2/addresses/adr_64ebe1918c3d11f19baa00224804dbec", - "status": 201, - "statusText": "Created" + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" }, - "startedDateTime": "2026-07-30T17:37:53.675Z", - "time": 195, + "startedDateTime": "2026-08-11T20:03:30.266Z", + "time": 206, "timings": { "blocked": -1, "connect": -1, @@ -308,7 +296,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 195 + "wait": 206 } } ], diff --git a/test/helpers/fixture.d.ts b/test/helpers/fixture.d.ts new file mode 100644 index 000000000..90ac5fb5b --- /dev/null +++ b/test/helpers/fixture.d.ts @@ -0,0 +1,62 @@ +import type AddressServiceFactory from '../../src/services/address_service'; +import type ParcelServiceFactory from '../../src/services/parcel_service'; +import type CustomsInfoServiceFactory from '../../src/services/customs_info_service'; +import type CustomsItemServiceFactory from '../../src/services/customs_item_service'; +import type ShipmentServiceFactory from '../../src/services/shipment_service'; + +type AddressCreateInput = Parameters['create']>[0]; +type ParcelCreateInput = Parameters['create']>[0]; +type CustomsInfoCreateInput = Parameters['create']>[0]; +type CustomsItemCreateInput = Parameters['create']>[0]; +type ShipmentCreateInput = Parameters['create']>[0]; + +declare class Fixture { + static readFixtureData(): Record; + static pageSize(): number; + + static uspsCarrierAccountId(): string; + static usps(): string; + static uspsService(): string; + static pickupService(): string; + static reportType(): string; + static reportDate(): string; + + static caAddress1(): AddressCreateInput; + static caAddress2(): AddressCreateInput; + static incorrectAddress(): AddressCreateInput; + + static basicParcel(): ParcelCreateInput; + static basicCustomsItem(): CustomsItemCreateInput; + static basicCustomsInfo(): CustomsInfoCreateInput; + static taxIdentifier(): Record; + + static basicShipment(): ShipmentCreateInput; + static fullShipment(): ShipmentCreateInput; + static oneCallBuyShipment(): ShipmentCreateInput & Record; + + static basicPickup(): Record; + static basicCarrierAccount(): Record; + static basicInsurance(): Record; + static basicClaim(): Record; + static basicOrder(): Record; + + static creditCardDetails(): Record; + static rmaFormOptions(): Record; + + static eventBody(): Buffer; + static webhookHmacSignature(): string; + static webhookSecret(): string; + static webhookUrl(): string; + static webhookCustomHeaders(): Record; + + static plannedShipDate(): string; + static plannedDeliveryDate(): string; + static billing(): Record; + + static lumaRulesetName(): string; + static lumaPlannedShipDate(): string; + + static referralUser(): Record; +} + +export default Fixture; diff --git a/test/services/address.test.js b/test/services/address.test.ts similarity index 91% rename from test/services/address.test.js rename to test/services/address.test.ts index 536fd8f70..2afa0cb4b 100644 --- a/test/services/address.test.js +++ b/test/services/address.test.ts @@ -1,13 +1,16 @@ -import { expect } from 'chai'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import EasyPostClient from '../../src/easypost'; import InvalidRequestError from '../../src/errors/api/invalid_request_error'; import EndOfPaginationError from '../../src/errors/general/end_of_pagination_error'; import Address from '../../src/models/address'; +import type AddressServiceFactory from '../../src/services/address_service'; import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; +type AddressTestCreateInput = Parameters['create']>[0]; + /* eslint-disable func-names */ describe('Address Service', function () { const getPolly = setupPolly.setupPollyTests(); @@ -31,7 +34,7 @@ describe('Address Service', function () { }); it('creates an address with verify param', async function () { - const addressData = Fixture.incorrectAddress(); + const addressData = Fixture.incorrectAddress() as AddressTestCreateInput; // Creating normally (without specifying "verify") will make the address and perform no verifications let address = await client.Address.create(addressData); @@ -40,7 +43,7 @@ describe('Address Service', function () { expect(address.verifications.delivery).to.be.undefined; // Creating with verify = true will make the address and perform verifications - addressData.verify = true; + addressData.verify = [true]; address = await client.Address.create(addressData); expect(address).to.be.an.instanceOf(Address); @@ -63,7 +66,7 @@ describe('Address Service', function () { }); it('creates an address with verify_strict param', async function () { - const addressData = Fixture.caAddress2(); + const addressData = Fixture.caAddress2() as AddressTestCreateInput; addressData.verify_strict = true; const address = await client.Address.create(addressData); @@ -74,7 +77,7 @@ describe('Address Service', function () { }); it('creates an address with an array verify param', async function () { - const addressData = Fixture.incorrectAddress(); + const addressData = Fixture.incorrectAddress() as AddressTestCreateInput; // Creating normally (without specifying "verify") will make the address, perform no verifications let address = await client.Address.create(addressData); @@ -83,7 +86,7 @@ describe('Address Service', function () { expect(address.verifications.delivery).to.be.undefined; // Creating with verify = true will make the address, perform verifications - addressData.verify = [true]; + addressData.verify = true; address = await client.Address.create(addressData); expect(address).to.be.an.instanceOf(Address); @@ -163,7 +166,7 @@ describe('Address Service', function () { }); it('creates an address with verify_carrier param', async function () { - const addressData = Fixture.incorrectAddress(); + const addressData = Fixture.incorrectAddress() as AddressTestCreateInput; addressData.verify = true; addressData.verify_carrier = 'UPS'; @@ -176,7 +179,7 @@ describe('Address Service', function () { }); it('creates and verifies address with verify_carrier param', async function () { - const addressData = Fixture.incorrectAddress(); + const addressData = Fixture.incorrectAddress() as AddressTestCreateInput; addressData.verify_carrier = 'UPS'; diff --git a/test/services/base_service.test.js b/test/services/base_service.test.ts similarity index 99% rename from test/services/base_service.test.js rename to test/services/base_service.test.ts index 302485994..8418dc6c8 100644 --- a/test/services/base_service.test.js +++ b/test/services/base_service.test.ts @@ -1,5 +1,5 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import EndOfPaginationError from '../../src/errors/general/end_of_pagination_error'; diff --git a/test/services/customs_info.test.js b/test/services/customs_info.test.ts similarity index 97% rename from test/services/customs_info.test.js rename to test/services/customs_info.test.ts index 7941ade71..c8a7ab183 100644 --- a/test/services/customs_info.test.js +++ b/test/services/customs_info.test.ts @@ -1,5 +1,5 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import CustomsInfo from '../../src/models/customs_info'; diff --git a/test/services/customs_item.test.js b/test/services/customs_item.test.ts similarity index 97% rename from test/services/customs_item.test.js rename to test/services/customs_item.test.ts index 14838cd2b..bcf44b06b 100644 --- a/test/services/customs_item.test.js +++ b/test/services/customs_item.test.ts @@ -1,5 +1,5 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +import { expect } from 'vitest'; import EasyPost from '../../src/easypost'; import CustomsItem from '../../src/models/customs_item'; diff --git a/test/services/parcel.test.js b/test/services/parcel.test.ts similarity index 97% rename from test/services/parcel.test.js rename to test/services/parcel.test.ts index 4746ce775..30e1e08ca 100644 --- a/test/services/parcel.test.js +++ b/test/services/parcel.test.ts @@ -1,5 +1,5 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import Parcel from '../../src/models/parcel'; diff --git a/test/services/shipment.test.js b/test/services/shipment.test.ts similarity index 98% rename from test/services/shipment.test.js rename to test/services/shipment.test.ts index 63f5593c3..aef8f0f5f 100644 --- a/test/services/shipment.test.js +++ b/test/services/shipment.test.ts @@ -1,4 +1,4 @@ -import { expect } from 'chai'; +import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import EndOfPaginationError from '../../src/errors/general/end_of_pagination_error'; @@ -231,7 +231,7 @@ describe('Shipment Service', function () { throw new Error('Test failed intentionally'); } catch (error) { expect(error).to.be.an.instanceOf(FilteringError); - expect(error.message).to.equal('No rates found.'); + expect(error instanceof Error ? error.message : String(error)).to.equal('No rates found.'); } }); @@ -245,7 +245,7 @@ describe('Shipment Service', function () { } catch (error) { expect(error).to.be.an.instanceOf(InvalidParameterError); const regex = /Invalid deliveryAccuracy value/; - expect(error.message).to.match(regex); + expect(error instanceof Error ? error.message : String(error)).to.match(regex); } }); diff --git a/tsconfig.test-services.json b/tsconfig.test-services.json new file mode 100644 index 000000000..e95c854fd --- /dev/null +++ b/tsconfig.test-services.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "allowJs": true, + "checkJs": false, + "noImplicitAny": false, + "noEmit": true, + "skipLibCheck": true, + "types": ["vitest/globals", "node"] + }, + "include": ["test/services/**/*.ts"] +} From 8a94ade4babfda0487f669687be11ca0e856c2ea Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:11:02 -0600 Subject: [PATCH 08/14] TSM-06: restore address verify assignments and cassettes --- .../recording.har | 56 ++++++++++------- .../recording.har | 60 +++++++++++-------- test/services/address.test.ts | 4 +- 3 files changed, 74 insertions(+), 46 deletions(-) diff --git a/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har b/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har index 9a0c8d29b..2f2465ce5 100644 --- a/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har +++ b/test/cassettes/Address-Service_1115845720/creates-an-address-with-an-array-verify-param_1154761701/recording.har @@ -160,11 +160,11 @@ } }, { - "_id": "d679a2160a9a61627941da5a2d70e9c3", + "_id": "54ac43ea33387e04e4be32a3e56f2d9d", "_order": 0, "cache": {}, "request": { - "bodySize": 189, + "bodySize": 191, "cookies": [], "headers": [ { @@ -185,29 +185,33 @@ }, { "name": "content-length", - "value": 189 + "value": 191 } ], - "headersSize": 320, + "headersSize": 392, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "params": [], - "text": "{\"verify\":true,\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" + "text": "{\"verify\":[true],\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" }, "queryString": [], "url": "https://api.easypost.com/v2/addresses" }, "response": { - "bodySize": 149, + "bodySize": 740, "content": { "mimeType": "application/json; charset=utf-8", - "size": 149, - "text": "{\"error\":{\"code\":\"APIKEY.INACTIVE\",\"message\":\"This api key is no longer active. Please use a different api key or reactivate this key.\",\"errors\":[]}}" + "size": 740, + "text": "{\"id\":\"adr_653f41368c3d11f194b8002248041e50\",\"object\":\"Address\",\"created_at\":\"2026-07-30T17:37:54Z\",\"updated_at\":\"2026-07-30T17:37:54Z\",\"name\":null,\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"street2\":null,\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{\"zip4\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":null},\"delivery\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":{}}}}" }, "cookies": [], "headers": [ + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, { "name": "content-encoding", "value": "gzip" @@ -220,6 +224,18 @@ "name": "easypost-api-version", "value": "2015-01-01" }, + { + "name": "expires", + "value": "0" + }, + { + "name": "location", + "value": "/api/v2/addresses/adr_653f41368c3d11f194b8002248041e50" + }, + { + "name": "pragma", + "value": "no-cache" + }, { "name": "referrer-policy", "value": "strict-origin-when-cross-origin" @@ -246,7 +262,7 @@ }, { "name": "x-ep-request-uuid", - "value": "d7dd9a7f6a7b8012e2bcdee301c53ab5" + "value": "d6c23b766a6b8bf2e0e306b103f2a796" }, { "name": "x-frame-options", @@ -254,7 +270,7 @@ }, { "name": "x-node", - "value": "bigweb57nuq" + "value": "bigweb38nuq" }, { "name": "x-permitted-cross-domain-policies", @@ -262,29 +278,29 @@ }, { "name": "x-proxied", - "value": "intlb4nuq d4a20a271b, extlb2nuq 1318c68dc0" + "value": "intlb5nuq 1bff4a8790, extlb1nuq e8c67c6320" }, { "name": "x-runtime", - "value": "0.010667" + "value": "0.068827" }, { "name": "x-version-label", - "value": "easypost-202608111901-7fd0066fa9-main" + "value": "easypost-202607300115-be0cf87c94-main" }, { "name": "x-xss-protection", "value": "1; mode=block" } ], - "headersSize": 649, + "headersSize": 789, "httpVersion": "HTTP/1.1", - "redirectURL": "", - "status": 403, - "statusText": "Forbidden" + "redirectURL": "/api/v2/addresses/adr_653f41368c3d11f194b8002248041e50", + "status": 201, + "statusText": "Created" }, - "startedDateTime": "2026-08-11T20:03:30.484Z", - "time": 46, + "startedDateTime": "2026-07-30T17:37:54.228Z", + "time": 189, "timings": { "blocked": -1, "connect": -1, @@ -292,7 +308,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 46 + "wait": 189 } } ], diff --git a/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har b/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har index 2f4237268..b04843a6f 100644 --- a/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har +++ b/test/cassettes/Address-Service_1115845720/creates-an-address-with-verify-param_348500273/recording.har @@ -160,11 +160,11 @@ } }, { - "_id": "54ac43ea33387e04e4be32a3e56f2d9d", + "_id": "d679a2160a9a61627941da5a2d70e9c3", "_order": 0, "cache": {}, "request": { - "bodySize": 191, + "bodySize": 189, "cookies": [], "headers": [ { @@ -185,29 +185,33 @@ }, { "name": "content-length", - "value": 191 + "value": 189 } ], - "headersSize": 320, + "headersSize": 392, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "params": [], - "text": "{\"verify\":[true],\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" + "text": "{\"verify\":true,\"address\":{\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"email\":\"test@example.com\",\"phone\":\"5555555555\"}}" }, "queryString": [], "url": "https://api.easypost.com/v2/addresses" }, "response": { - "bodySize": 149, + "bodySize": 740, "content": { "mimeType": "application/json; charset=utf-8", - "size": 149, - "text": "{\"error\":{\"code\":\"APIKEY.INACTIVE\",\"message\":\"This api key is no longer active. Please use a different api key or reactivate this key.\",\"errors\":[]}}" + "size": 740, + "text": "{\"id\":\"adr_64ebe1918c3d11f19baa00224804dbec\",\"object\":\"Address\",\"created_at\":\"2026-07-30T17:37:53Z\",\"updated_at\":\"2026-07-30T17:37:53Z\",\"name\":null,\"company\":\"EasyPost\",\"street1\":\"000 unknown street\",\"street2\":null,\"city\":\"Not A City\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{\"zip4\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":null},\"delivery\":{\"success\":false,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":{}}}}" }, "cookies": [], "headers": [ + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, { "name": "content-encoding", "value": "gzip" @@ -220,6 +224,18 @@ "name": "easypost-api-version", "value": "2015-01-01" }, + { + "name": "expires", + "value": "0" + }, + { + "name": "location", + "value": "/api/v2/addresses/adr_64ebe1918c3d11f19baa00224804dbec" + }, + { + "name": "pragma", + "value": "no-cache" + }, { "name": "referrer-policy", "value": "strict-origin-when-cross-origin" @@ -236,10 +252,6 @@ "name": "x-backend", "value": "easypost" }, - { - "name": "x-canary", - "value": "direct" - }, { "name": "x-content-type-options", "value": "nosniff" @@ -250,7 +262,7 @@ }, { "name": "x-ep-request-uuid", - "value": "d7dd9a7f6a7b8012e2bcdee301c53a9b" + "value": "d6c23b766a6b8bf1e0e306b103f2a66d" }, { "name": "x-frame-options", @@ -258,7 +270,7 @@ }, { "name": "x-node", - "value": "bigweb43nuq" + "value": "bigweb64nuq" }, { "name": "x-permitted-cross-domain-policies", @@ -266,29 +278,29 @@ }, { "name": "x-proxied", - "value": "intlb3nuq d4a20a271b, extlb2nuq 1318c68dc0" + "value": "intlb4nuq 1bff4a8790, extlb1nuq e8c67c6320" }, { "name": "x-runtime", - "value": "0.012801" + "value": "0.069579" }, { "name": "x-version-label", - "value": "easypost-202608111901-7fd0066fa9-main" + "value": "easypost-202607300115-be0cf87c94-main" }, { "name": "x-xss-protection", "value": "1; mode=block" } ], - "headersSize": 667, + "headersSize": 789, "httpVersion": "HTTP/1.1", - "redirectURL": "", - "status": 403, - "statusText": "Forbidden" + "redirectURL": "/api/v2/addresses/adr_64ebe1918c3d11f19baa00224804dbec", + "status": 201, + "statusText": "Created" }, - "startedDateTime": "2026-08-11T20:03:30.266Z", - "time": 206, + "startedDateTime": "2026-07-30T17:37:53.675Z", + "time": 195, "timings": { "blocked": -1, "connect": -1, @@ -296,7 +308,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 206 + "wait": 195 } } ], diff --git a/test/services/address.test.ts b/test/services/address.test.ts index 2afa0cb4b..fedbe4706 100644 --- a/test/services/address.test.ts +++ b/test/services/address.test.ts @@ -43,7 +43,7 @@ describe('Address Service', function () { expect(address.verifications.delivery).to.be.undefined; // Creating with verify = true will make the address and perform verifications - addressData.verify = [true]; + addressData.verify = true; address = await client.Address.create(addressData); expect(address).to.be.an.instanceOf(Address); @@ -86,7 +86,7 @@ describe('Address Service', function () { expect(address.verifications.delivery).to.be.undefined; // Creating with verify = true will make the address, perform verifications - addressData.verify = true; + addressData.verify = [true]; address = await client.Address.create(addressData); expect(address).to.be.an.instanceOf(Address); From 7cf53aafa3446dc71862df01842763e78a919c4f Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:17:52 -0600 Subject: [PATCH 09/14] TSM-06: add source-derived fixture type assertions in tests --- test/services/address.test.ts | 13 +++--- test/services/customs_info.test.ts | 9 +++- test/services/customs_item.test.ts | 9 +++- test/services/parcel.test.ts | 9 +++- test/services/shipment.test.ts | 70 +++++++++++++++++------------- 5 files changed, 69 insertions(+), 41 deletions(-) diff --git a/test/services/address.test.ts b/test/services/address.test.ts index fedbe4706..d7ded88fc 100644 --- a/test/services/address.test.ts +++ b/test/services/address.test.ts @@ -10,6 +10,7 @@ import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; type AddressTestCreateInput = Parameters['create']>[0]; +type AddressTestCreateAndVerifyInput = Parameters['createAndVerify']>[0]; /* eslint-disable func-names */ describe('Address Service', function () { @@ -26,7 +27,7 @@ describe('Address Service', function () { }); it('creates an address', async function () { - const address = await client.Address.create(Fixture.caAddress1()); + const address = await client.Address.create(Fixture.caAddress1() as AddressTestCreateInput); expect(address).to.be.an.instanceOf(Address); expect(address.id).to.match(/^adr_/); @@ -94,7 +95,7 @@ describe('Address Service', function () { }); it('retrieves an address', async function () { - const address = await client.Address.create(Fixture.caAddress1()); + const address = await client.Address.create(Fixture.caAddress1() as AddressTestCreateInput); const retrievedAddress = await client.Address.retrieve(address.id); expect(retrievedAddress).to.be.an.instanceOf(Address); @@ -130,7 +131,7 @@ describe('Address Service', function () { }); it('creates a verified address', async function () { - const addressData = Fixture.caAddress2(); + const addressData = Fixture.caAddress2() as AddressTestCreateAndVerifyInput; const address = await client.Address.createAndVerify(addressData); @@ -140,7 +141,7 @@ describe('Address Service', function () { }); it('throws an error when we cannot create and verify an address', async function () { - const addressData = Fixture.incorrectAddress(); + const addressData = Fixture.incorrectAddress() as AddressTestCreateAndVerifyInput; // Creates with verify = true behind the scenes, will throw an error if the address cannot be verified return client.Address.createAndVerify(addressData).catch((err) => @@ -149,7 +150,7 @@ describe('Address Service', function () { }); it('verifies an address', async function () { - const address = await client.Address.create(Fixture.caAddress2()); + const address = await client.Address.create(Fixture.caAddress2() as AddressTestCreateInput); const verifiedAddress = await client.Address.verifyAddress(address.id); expect(verifiedAddress).to.be.an.instanceOf(Address); @@ -179,7 +180,7 @@ describe('Address Service', function () { }); it('creates and verifies address with verify_carrier param', async function () { - const addressData = Fixture.incorrectAddress() as AddressTestCreateInput; + const addressData = Fixture.incorrectAddress() as AddressTestCreateAndVerifyInput; addressData.verify_carrier = 'UPS'; diff --git a/test/services/customs_info.test.ts b/test/services/customs_info.test.ts index c8a7ab183..48a882a59 100644 --- a/test/services/customs_info.test.ts +++ b/test/services/customs_info.test.ts @@ -3,10 +3,13 @@ import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import CustomsInfo from '../../src/models/customs_info'; +import type CustomsInfoServiceFactory from '../../src/services/customs_info_service'; import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; +type CustomsInfoTestCreateInput = Parameters['create']>[0]; + describe('CustomsInfo Service', function () { const getPolly = setupPolly.setupPollyTests(); let client; @@ -21,7 +24,8 @@ describe('CustomsInfo Service', function () { }); it('creates a customs info', async function () { - const customsInfo = await client.CustomsInfo.create(Fixture.basicCustomsInfo()); + const customsInfoData = Fixture.basicCustomsInfo() as CustomsInfoTestCreateInput; + const customsInfo = await client.CustomsInfo.create(customsInfoData); expect(customsInfo).to.be.an.instanceOf(CustomsInfo); expect(customsInfo.id).to.match(/^cstinfo_/); @@ -29,7 +33,8 @@ describe('CustomsInfo Service', function () { }); it('retrieves a customs info', async function () { - const customsInfo = await client.CustomsInfo.create(Fixture.basicCustomsInfo()); + const customsInfoData = Fixture.basicCustomsInfo() as CustomsInfoTestCreateInput; + const customsInfo = await client.CustomsInfo.create(customsInfoData); const retrievedCustomsInfo = await client.CustomsInfo.retrieve(customsInfo.id); expect(customsInfo).to.be.an.instanceOf(CustomsInfo); diff --git a/test/services/customs_item.test.ts b/test/services/customs_item.test.ts index bcf44b06b..8957693d4 100644 --- a/test/services/customs_item.test.ts +++ b/test/services/customs_item.test.ts @@ -3,10 +3,13 @@ import { expect } from 'vitest'; import EasyPost from '../../src/easypost'; import CustomsItem from '../../src/models/customs_item'; +import type CustomsItemServiceFactory from '../../src/services/customs_item_service'; import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; +type CustomsItemTestCreateInput = Parameters['create']>[0]; + describe('CustomsItem Service', function () { const getPolly = setupPolly.setupPollyTests(); let client; @@ -21,7 +24,8 @@ describe('CustomsItem Service', function () { }); it('creates a customs item', async function () { - const customsItem = await client.CustomsItem.create(Fixture.basicCustomsItem()); + const customsItemData = Fixture.basicCustomsItem() as CustomsItemTestCreateInput; + const customsItem = await client.CustomsItem.create(customsItemData); expect(customsItem).to.be.an.instanceOf(CustomsItem); expect(customsItem.id).to.match(/^cstitem_/); @@ -29,7 +33,8 @@ describe('CustomsItem Service', function () { }); it('retrieves a customs item', async function () { - const customsItem = await client.CustomsItem.create(Fixture.basicCustomsItem()); + const customsItemData = Fixture.basicCustomsItem() as CustomsItemTestCreateInput; + const customsItem = await client.CustomsItem.create(customsItemData); const retrievedCustomsInfo = await client.CustomsItem.retrieve(customsItem.id); expect(customsItem).to.be.an.instanceOf(CustomsItem); diff --git a/test/services/parcel.test.ts b/test/services/parcel.test.ts index 30e1e08ca..a89f20e27 100644 --- a/test/services/parcel.test.ts +++ b/test/services/parcel.test.ts @@ -3,10 +3,13 @@ import { expect } from 'vitest'; import EasyPostClient from '../../src/easypost'; import Parcel from '../../src/models/parcel'; +import type ParcelServiceFactory from '../../src/services/parcel_service'; import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; +type ParcelTestCreateInput = Parameters['create']>[0]; + describe('Parcel Service', function () { const getPolly = setupPolly.setupPollyTests(); let client; @@ -21,7 +24,8 @@ describe('Parcel Service', function () { }); it('creates a parcel', async function () { - const parcel = await client.Parcel.create(Fixture.basicParcel()); + const parcelData = Fixture.basicParcel() as ParcelTestCreateInput; + const parcel = await client.Parcel.create(parcelData); expect(parcel).to.be.an.instanceOf(Parcel); expect(parcel.id).to.match(/^prcl_/); @@ -29,7 +33,8 @@ describe('Parcel Service', function () { }); it('retrieves a parcel', async function () { - const parcel = await client.Parcel.create(Fixture.basicParcel()); + const parcelData = Fixture.basicParcel() as ParcelTestCreateInput; + const parcel = await client.Parcel.create(parcelData); const retrievedParcel = await client.Parcel.retrieve(parcel.id); expect(parcel).to.be.an.instanceOf(Parcel); diff --git a/test/services/shipment.test.ts b/test/services/shipment.test.ts index aef8f0f5f..09353d3c3 100644 --- a/test/services/shipment.test.ts +++ b/test/services/shipment.test.ts @@ -6,9 +6,21 @@ import FilteringError from '../../src/errors/general/filtering_error'; import InvalidParameterError from '../../src/errors/general/invalid_parameter_error'; import Rate from '../../src/models/rate'; import Shipment from '../../src/models/shipment'; +import type AddressServiceFactory from '../../src/services/address_service'; +import type EndShipperServiceFactory from '../../src/services/end_shipper_service'; +import type ParcelServiceFactory from '../../src/services/parcel_service'; +import type ShipmentServiceFactory from '../../src/services/shipment_service'; import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; +type AddressTestCreateInput = Parameters['create']>[0]; +type EndShipperTestCreateInput = Parameters['create']>[0]; +type ParcelTestCreateInput = Parameters['create']>[0]; +type ShipmentTestCreateInput = Parameters['create']>[0]; +type ShipmentTestCreateAndBuyLumaInput = + Parameters['createAndBuyLuma']>[0]; +type ShipmentTestGenerateFormInput = Parameters['generateForm']>[2]; + /* eslint-disable func-names */ describe('Shipment Service', function () { const getPolly = setupPolly.setupPollyTests(); @@ -24,7 +36,7 @@ describe('Shipment Service', function () { }); it('creates a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); expect(shipment).to.be.an.instanceOf(Shipment); expect(shipment.id).to.match(/^shp_/); @@ -35,7 +47,7 @@ describe('Shipment Service', function () { }); it('creates a shipment with empty or null objects and arrays', async function () { - const shipmentData = Fixture.basicShipment(); + const shipmentData = Fixture.basicShipment() as ShipmentTestCreateInput; shipmentData.customs_info = []; shipmentData.options = null; shipmentData.tax_identifiers = undefined; @@ -52,7 +64,7 @@ describe('Shipment Service', function () { }); it('creates a shipment with tax_identifiers', async function () { - const shipmentData = Fixture.basicShipment(); + const shipmentData = Fixture.basicShipment() as ShipmentTestCreateInput; shipmentData.tax_identifiers = [Fixture.taxIdentifier()]; const shipment = await client.Shipment.create(shipmentData); @@ -63,9 +75,9 @@ describe('Shipment Service', function () { }); it('creates a shipment when only IDs are used', async function () { - const fromAddress = await client.Address.create(Fixture.caAddress1()); - const toAddress = await client.Address.create(Fixture.caAddress2()); - const parcel = await client.Parcel.create(Fixture.basicParcel()); + const fromAddress = await client.Address.create(Fixture.caAddress1() as AddressTestCreateInput); + const toAddress = await client.Address.create(Fixture.caAddress2() as AddressTestCreateInput); + const parcel = await client.Parcel.create(Fixture.basicParcel() as ParcelTestCreateInput); const shipment = await client.Shipment.create({ from_address: { id: fromAddress.id }, @@ -82,7 +94,7 @@ describe('Shipment Service', function () { }); it('retrieves a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); const retrievedShipment = await client.Shipment.retrieve(shipment.id); @@ -121,7 +133,7 @@ describe('Shipment Service', function () { }); it('buys a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate()); @@ -129,7 +141,7 @@ describe('Shipment Service', function () { }); it('regenerates rates for a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); const rates = await client.Shipment.regenerateRates(shipment.id); @@ -142,7 +154,7 @@ describe('Shipment Service', function () { }); it('converts the label format of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate()); @@ -154,7 +166,7 @@ describe('Shipment Service', function () { it('insures a shipment', async function () { // If the shipment was purchased with a USPS rate, it must have its insurance set to `0` when bought // so that USPS doesn't automatically insure it so we could manually insure it here. - const shipmentData = Fixture.oneCallBuyShipment(); + const shipmentData = Fixture.oneCallBuyShipment() as ShipmentTestCreateInput; shipmentData.insurance = '0'; const shipment = await client.Shipment.create(shipmentData); @@ -168,7 +180,7 @@ describe('Shipment Service', function () { // Refunding a test shipment must happen within seconds of the shipment being created as test shipments naturally // follow a flow of created -> delivered to cycle through tracking events in test mode - as such anything older // than a few seconds in test mode may not be refundable. - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment()); + const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); const refundedShipment = await client.Shipment.refund(shipment.id); @@ -176,7 +188,7 @@ describe('Shipment Service', function () { }); it('retrieves smartRates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment()); + const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); expect(shipment.rates).to.exist; @@ -192,7 +204,7 @@ describe('Shipment Service', function () { }); it('gets the lowest rate', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment()); + const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); // Test lowest rate with no filters const lowestRate = shipment.lowestRate(); @@ -213,7 +225,7 @@ describe('Shipment Service', function () { }); it('gets the lowest smartrate', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); // Test lowest smartrate with valid filters const lowestSmartRate = await client.Shipment.lowestSmartRate(shipment.id, 3, 'percentile_90'); @@ -223,7 +235,7 @@ describe('Shipment Service', function () { }); it('raises an error for lowestSmartRate when no rates are found due to deliveryDays', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); // Test lowest smartrate with invalid filters (should error due to strict deliveryDays) try { @@ -236,7 +248,7 @@ describe('Shipment Service', function () { }); it('raises an error for lowestSmartRate when no rates are found due to deliveryAccuracy', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); // Test lowest smartrate with invalid filters (should error due to invalid deliveryAccuracy) try { @@ -250,7 +262,7 @@ describe('Shipment Service', function () { }); it('gets the lowest smartrate from a list of smartRates', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with valid filters @@ -261,7 +273,7 @@ describe('Shipment Service', function () { }); it('raises an error for getLowestSmartRate when no rates are found due to deliveryDays', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with invalid filters (should error due to strict deliveryDays) @@ -271,7 +283,7 @@ describe('Shipment Service', function () { }); it('raises an error for getLowestSmartRate when no rates are found due to deliveryAccuracy', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with invalid filters (should error due to invalid deliveryAccuracy) @@ -284,14 +296,14 @@ describe('Shipment Service', function () { }); it('generates a form for a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment()); + const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); const formType = 'return_packing_slip'; const shipmentWithForm = await client.Shipment.generateForm( shipment.id, formType, - Fixture.rmaFormOptions(), + Fixture.rmaFormOptions() as ShipmentTestGenerateFormInput, ); expect(shipmentWithForm.forms.length).to.equal(1); @@ -303,16 +315,16 @@ describe('Shipment Service', function () { }); it('buys a shipment with insuranceAmount', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate(), 100); expect(boughtShipment.insurance).to.equal('100.00'); }); it('buys a shipment with end_shipper_id', async function () { - const endShipper = await client.EndShipper.create(Fixture.caAddress1()); + const endShipper = await client.EndShipper.create(Fixture.caAddress1() as EndShipperTestCreateInput); - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const boughtShipment = await client.Shipment.buy( shipment.id, shipment.lowestRate(), @@ -324,7 +336,7 @@ describe('Shipment Service', function () { }); it('retrieve estimated delivery dates for each of the Rates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const estimatedDeliveryDates = await client.Shipment.retrieveEstimatedDeliveryDate( shipment.id, Fixture.plannedShipDate(), @@ -338,7 +350,7 @@ describe('Shipment Service', function () { }); it('retrieve recommended ship dates for each of the Rates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const recommendedShipDates = await client.Shipment.recommendShipDate( shipment.id, Fixture.plannedDeliveryDate(), @@ -352,7 +364,7 @@ describe('Shipment Service', function () { }); it('creates and buys a Shipment with Luma', async function () { - const oneCallBuyShipment = Fixture.oneCallBuyShipment(); + const oneCallBuyShipment = Fixture.oneCallBuyShipment() as ShipmentTestCreateAndBuyLumaInput; delete oneCallBuyShipment.service; oneCallBuyShipment.ruleset_name = Fixture.lumaRulesetName(); oneCallBuyShipment.planned_ship_date = Fixture.lumaPlannedShipDate(); @@ -363,7 +375,7 @@ describe('Shipment Service', function () { }); it('buys a Shipment with Luma', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment()); + const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); const boughtShipment = await client.Shipment.buyLuma(shipment.id, { ruleset_name: Fixture.lumaRulesetName(), From 779d139d4d11c5c0fc96710c325e34a8e5a700b1 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:23:40 -0600 Subject: [PATCH 10/14] TSM-06: include vitest globals in tsconfig build --- tsconfig.build.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index f4bfc21ac..f46983efb 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -5,7 +5,8 @@ "checkJs": false, "noImplicitAny": false, "declaration": true, - "noEmit": true + "noEmit": true, + "types": ["vitest/globals", "node"] }, "include": ["src/**/*.js", "src/**/*.ts", "test/**/*.js", "test/**/*.ts"], "exclude": ["dist/**", "docs/**", "coverage/**", "node_modules/**"] From b46082db6608f5d5f6256453a2e49785fde52299 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 12 Aug 2026 10:53:33 -0600 Subject: [PATCH 11/14] TSM-06: type service return values to models and collections --- src/services/address_service.ts | 17 +++++++---- src/services/customs_info_service.ts | 5 ++-- src/services/customs_item_service.ts | 5 ++-- src/services/parcel_service.ts | 5 ++-- src/services/shipment_service.ts | 42 +++++++++++++++++----------- 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/services/address_service.ts b/src/services/address_service.ts index c16e6326a..393388f2c 100644 --- a/src/services/address_service.ts +++ b/src/services/address_service.ts @@ -1,4 +1,5 @@ import baseService from './base_service'; +import Address from '../models/address'; type AddressCreateParameters = Record & { name?: string | null; @@ -20,6 +21,7 @@ type AddressCreateParameters = Record & { }; type PaginationCollection = Record; +type AddressCollection = { addresses: Address[]; has_more: boolean }; export default (easypostClient) => /** @@ -33,7 +35,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created address. */ - static async create(params: AddressCreateParameters): Promise { + static async create(params: AddressCreateParameters): Promise
{ const url = 'addresses'; const wrappedParams: Record = {}; @@ -64,7 +66,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the address to be created. * @returns {Address} - The created and verified address. */ - static async createAndVerify(params: AddressCreateParameters): Promise { + static async createAndVerify(params: AddressCreateParameters): Promise
{ const url = `addresses/create_and_verify`; const wrappedParams: Record = {}; @@ -91,7 +93,7 @@ export default (easypostClient) => * @param {Object} [params] - Parameters to filter the list of addresses. * @returns {Object} - An object containing a list of {@link Address addresses} and pagination information. */ - static async all(params: Record = {}): Promise { + static async all(params: Record = {}): Promise { const url = 'addresses'; return this._all(url, params); @@ -103,7 +105,10 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage(addresses: PaginationCollection, pageSize?: number): Promise { + static async getNextPage( + addresses: PaginationCollection, + pageSize?: number, + ): Promise { const url = 'addresses'; return this._getNextPage(url, 'addresses', addresses, pageSize); } @@ -114,7 +119,7 @@ export default (easypostClient) => * @param {string} id - The ID of the address to retrieve. * @returns {Address} - The retrieved address. */ - static async retrieve(id: string): Promise { + static async retrieve(id: string): Promise
{ const url = `addresses/${id}`; return this._retrieve(url); @@ -126,7 +131,7 @@ export default (easypostClient) => * @param {string} id - The ID of the address to verify. * @returns {Address} - The verified address. */ - static async verifyAddress(id: string): Promise { + static async verifyAddress(id: string): Promise
{ try { const url = `addresses/${id}/verify`; const response = await easypostClient._get(url); diff --git a/src/services/customs_info_service.ts b/src/services/customs_info_service.ts index ce450138c..c7693e892 100644 --- a/src/services/customs_info_service.ts +++ b/src/services/customs_info_service.ts @@ -1,4 +1,5 @@ import baseService from './base_service'; +import CustomsInfo from '../models/customs_info'; type CustomsItemInput = Record; @@ -27,7 +28,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs info to be created. * @returns {CustomsInfo} - The created customs info. */ - static async create(params: CustomsInfoCreateParameters): Promise { + static async create(params: CustomsInfoCreateParameters): Promise { const url = 'customs_infos'; const wrappedParams = { @@ -43,7 +44,7 @@ export default (easypostClient) => * @param {string} id - The ID of the customs info to retrieve. * @returns {CustomsInfo} - The retrieved customs info. */ - static async retrieve(id: string): Promise { + static async retrieve(id: string): Promise { const url = `customs_infos/${id}`; return this._retrieve(url); diff --git a/src/services/customs_item_service.ts b/src/services/customs_item_service.ts index 7d6c69ca1..0b2d0087e 100644 --- a/src/services/customs_item_service.ts +++ b/src/services/customs_item_service.ts @@ -1,4 +1,5 @@ import baseService from './base_service'; +import CustomsItem from '../models/customs_item'; type CustomsItemCreateParameters = Record & { description?: string | null; @@ -23,7 +24,7 @@ export default (easypostClient) => * @param {Object} params - Parameters for the customs item to be created. * @returns {CustomsItem} - The created customs item. */ - static async create(params: CustomsItemCreateParameters): Promise { + static async create(params: CustomsItemCreateParameters): Promise { const url = 'customs_items'; const wrappedParams = { @@ -39,7 +40,7 @@ export default (easypostClient) => * @param {string} id - The ID of the customs item to retrieve. * @returns {CustomsItem} - The retrieved customs item. */ - static async retrieve(id: string): Promise { + static async retrieve(id: string): Promise { const url = `customs_items/${id}`; return this._retrieve(url); diff --git a/src/services/parcel_service.ts b/src/services/parcel_service.ts index 4945871c2..00a7a9c69 100644 --- a/src/services/parcel_service.ts +++ b/src/services/parcel_service.ts @@ -1,4 +1,5 @@ import baseService from './base_service'; +import Parcel from '../models/parcel'; type ParcelCreateParameters = Record & { length?: number | null; @@ -20,7 +21,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a parcel with. * @returns {Parcel} - The created parcel. */ - static async create(params: ParcelCreateParameters): Promise { + static async create(params: ParcelCreateParameters): Promise { const url = 'parcels'; const wrappedParams = { @@ -36,7 +37,7 @@ export default (easypostClient) => * @param {string} id - The ID of the parcel to retrieve. * @returns {Parcel} - The retrieved parcel. */ - static async retrieve(id: string): Promise { + static async retrieve(id: string): Promise { const url = `parcels/${id}`; return this._retrieve(url); diff --git a/src/services/shipment_service.ts b/src/services/shipment_service.ts index c7cb70d24..3cf34fa0a 100644 --- a/src/services/shipment_service.ts +++ b/src/services/shipment_service.ts @@ -1,5 +1,7 @@ import Constants from '../constants'; import baseService from './base_service'; +import Rate from '../models/rate'; +import Shipment from '../models/shipment'; type AddressCreateInput = Record & { verify?: boolean | string | string[] | null; @@ -40,6 +42,8 @@ type ShipmentCreateParameters = Record & { }; type ShipmentRateInput = string | { id: string }; type ShipmentCollection = Record; +type ShipmentListResponse = { shipments: Shipment[]; has_more: boolean }; +type ShipmentSmartRateResponse = Array>; export default (easypostClient) => /** @@ -53,7 +57,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create a shipment with. * @returns {Shipment} - The created shipment. */ - static async create(params: ShipmentCreateParameters): Promise { + static async create(params: ShipmentCreateParameters): Promise { const url = 'shipments'; const wrappedParams = { @@ -77,7 +81,7 @@ export default (easypostClient) => rate: ShipmentRateInput, insuranceAmount: number | null = null, endShipperId: string | null = null, - ): Promise { + ): Promise { let rateId = rate; if (typeof rate === 'object') { @@ -116,7 +120,7 @@ export default (easypostClient) => * @param {string} format - The format to convert the label to. * @returns {Shipment} - The shipment with the converted label format. */ - static async convertLabelFormat(id: string, format: string): Promise { + static async convertLabelFormat(id: string, format: string): Promise { const url = `shipments/${id}/label`; const wrappedParams = { file_format: format }; @@ -135,7 +139,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to regenerate rates for. * @returns {Shipment} - The shipment with regenerated rates. */ - static async regenerateRates(id: string): Promise { + static async regenerateRates(id: string): Promise { const url = `shipments/${id}/rerate`; const wrappedParams = {}; @@ -154,7 +158,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to get SmartRates for. * @returns {Rate[]} - The SmartRates for the shipment. */ - static async getSmartRates(id: string): Promise { + static async getSmartRates(id: string): Promise { const url = `shipments/${id}/smartrate`; try { @@ -173,7 +177,7 @@ export default (easypostClient) => * @param {number|string} amount - The amount to insure the shipment for. * @returns {Shipment} - The insured shipment. */ - static async insure(id: string, amount: number | string): Promise { + static async insure(id: string, amount: number | string): Promise { const url = `shipments/${id}/insure`; const wrappedParams = { amount }; @@ -198,7 +202,7 @@ export default (easypostClient) => id: string, formType: string, formOptions: Record = {}, - ): Promise { + ): Promise { const url = `shipments/${id}/forms`; const wrappedParams = { form: { @@ -222,7 +226,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to refund. * @returns {Shipment} - The refunded shipment. */ - static async refund(id: string): Promise { + static async refund(id: string): Promise { const url = `shipments/${id}/refund`; try { @@ -245,7 +249,7 @@ export default (easypostClient) => id: string, deliveryDays: number, deliveryAccuracy: string, - ): Promise { + ): Promise { const smartRates = (await this.getSmartRates(id)) as any[]; return Constants.Utils.getLowestSmartRate( smartRates, @@ -260,7 +264,7 @@ export default (easypostClient) => * @param {Object} [params] - Parameters to filter the shipments by. * @returns {Object} - An object containing a list of {@link Shipment shipments} and pagination information. */ - static async all(params: Record = {}): Promise { + static async all(params: Record = {}): Promise { const url = 'shipments'; return this._all(url, params); @@ -272,7 +276,10 @@ export default (easypostClient) => * @param {Number} pageSize The number of records to return on each page * @returns {EasyPostObject|Promise} The retrieved {@link EasyPostObject}-based class instance, or a `Promise` that rejects with an error. */ - static async getNextPage(shipments: ShipmentCollection, pageSize?: number): Promise { + static async getNextPage( + shipments: ShipmentCollection, + pageSize?: number, + ): Promise { const url = 'shipments'; return this._getNextPage(url, 'shipments', shipments, pageSize); @@ -284,7 +291,7 @@ export default (easypostClient) => * @param {string} id - The ID of the shipment to retrieve. * @returns {Shipment} - The shipment with the given ID. */ - static async retrieve(id: string): Promise { + static async retrieve(id: string): Promise { const url = `shipments/${id}`; return this._retrieve(url); @@ -299,7 +306,7 @@ export default (easypostClient) => static async retrieveEstimatedDeliveryDate( id: string, plannedShipDate: string, - ): Promise { + ): Promise { const url = `shipments/${id}/smartrate/delivery_date`; const wrappedParams = { @@ -321,7 +328,10 @@ export default (easypostClient) => * @param desiredDeliveryDate - The desired delivery date for the shipment. * @returns {Array} - An array of the recommended ship date and rates. */ - static async recommendShipDate(id: string, desiredDeliveryDate: string): Promise { + static async recommendShipDate( + id: string, + desiredDeliveryDate: string, + ): Promise { const url = `shipments/${id}/smartrate/precision_shipping`; const wrappedParams = { @@ -342,7 +352,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to create and buy a Shipment with Luma. * @returns {Shipment} - The shipment with the given ID. */ - static async createAndBuyLuma(params: ShipmentCreateParameters): Promise { + static async createAndBuyLuma(params: ShipmentCreateParameters): Promise { const url = `shipments/luma`; const wrappedParams = { @@ -364,7 +374,7 @@ export default (easypostClient) => * @param {Object} params - The parameters to buy a Shipment with Luma. * @returns {Shipment} - The shipment with the given ID. */ - static async buyLuma(id: string, params: Record): Promise { + static async buyLuma(id: string, params: Record): Promise { const url = `shipments/${id}/luma`; try { From 8634aaf2d178c9bf9a15550f11cab4a7959164a1 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:09:17 -0600 Subject: [PATCH 12/14] Format service TypeScript tests --- test/services/address.test.ts | 4 +- test/services/customs_info.test.ts | 4 +- test/services/customs_item.test.ts | 4 +- test/services/shipment.test.ts | 97 ++++++++++++++++++++++-------- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/test/services/address.test.ts b/test/services/address.test.ts index d7ded88fc..25e5f3521 100644 --- a/test/services/address.test.ts +++ b/test/services/address.test.ts @@ -10,7 +10,9 @@ import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; type AddressTestCreateInput = Parameters['create']>[0]; -type AddressTestCreateAndVerifyInput = Parameters['createAndVerify']>[0]; +type AddressTestCreateAndVerifyInput = Parameters< + ReturnType['createAndVerify'] +>[0]; /* eslint-disable func-names */ describe('Address Service', function () { diff --git a/test/services/customs_info.test.ts b/test/services/customs_info.test.ts index 48a882a59..c461a53a8 100644 --- a/test/services/customs_info.test.ts +++ b/test/services/customs_info.test.ts @@ -8,7 +8,9 @@ import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; -type CustomsInfoTestCreateInput = Parameters['create']>[0]; +type CustomsInfoTestCreateInput = Parameters< + ReturnType['create'] +>[0]; describe('CustomsInfo Service', function () { const getPolly = setupPolly.setupPollyTests(); diff --git a/test/services/customs_item.test.ts b/test/services/customs_item.test.ts index 8957693d4..436bb18e4 100644 --- a/test/services/customs_item.test.ts +++ b/test/services/customs_item.test.ts @@ -8,7 +8,9 @@ import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; import { withoutParams } from '../helpers/utils'; -type CustomsItemTestCreateInput = Parameters['create']>[0]; +type CustomsItemTestCreateInput = Parameters< + ReturnType['create'] +>[0]; describe('CustomsItem Service', function () { const getPolly = setupPolly.setupPollyTests(); diff --git a/test/services/shipment.test.ts b/test/services/shipment.test.ts index 09353d3c3..cc4b6a689 100644 --- a/test/services/shipment.test.ts +++ b/test/services/shipment.test.ts @@ -14,12 +14,17 @@ import Fixture from '../helpers/fixture'; import * as setupPolly from '../helpers/setup_polly'; type AddressTestCreateInput = Parameters['create']>[0]; -type EndShipperTestCreateInput = Parameters['create']>[0]; +type EndShipperTestCreateInput = Parameters< + ReturnType['create'] +>[0]; type ParcelTestCreateInput = Parameters['create']>[0]; type ShipmentTestCreateInput = Parameters['create']>[0]; -type ShipmentTestCreateAndBuyLumaInput = - Parameters['createAndBuyLuma']>[0]; -type ShipmentTestGenerateFormInput = Parameters['generateForm']>[2]; +type ShipmentTestCreateAndBuyLumaInput = Parameters< + ReturnType['createAndBuyLuma'] +>[0]; +type ShipmentTestGenerateFormInput = Parameters< + ReturnType['generateForm'] +>[2]; /* eslint-disable func-names */ describe('Shipment Service', function () { @@ -36,7 +41,9 @@ describe('Shipment Service', function () { }); it('creates a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); expect(shipment).to.be.an.instanceOf(Shipment); expect(shipment.id).to.match(/^shp_/); @@ -94,7 +101,9 @@ describe('Shipment Service', function () { }); it('retrieves a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); const retrievedShipment = await client.Shipment.retrieve(shipment.id); @@ -133,7 +142,9 @@ describe('Shipment Service', function () { }); it('buys a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate()); @@ -141,7 +152,9 @@ describe('Shipment Service', function () { }); it('regenerates rates for a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); const rates = await client.Shipment.regenerateRates(shipment.id); @@ -154,7 +167,9 @@ describe('Shipment Service', function () { }); it('converts the label format of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate()); @@ -180,7 +195,9 @@ describe('Shipment Service', function () { // Refunding a test shipment must happen within seconds of the shipment being created as test shipments naturally // follow a flow of created -> delivered to cycle through tracking events in test mode - as such anything older // than a few seconds in test mode may not be refundable. - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.oneCallBuyShipment() as ShipmentTestCreateInput, + ); const refundedShipment = await client.Shipment.refund(shipment.id); @@ -188,7 +205,9 @@ describe('Shipment Service', function () { }); it('retrieves smartRates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.oneCallBuyShipment() as ShipmentTestCreateInput, + ); expect(shipment.rates).to.exist; @@ -204,7 +223,9 @@ describe('Shipment Service', function () { }); it('gets the lowest rate', async function () { - const shipment = await client.Shipment.create(Fixture.fullShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.fullShipment() as ShipmentTestCreateInput, + ); // Test lowest rate with no filters const lowestRate = shipment.lowestRate(); @@ -225,7 +246,9 @@ describe('Shipment Service', function () { }); it('gets the lowest smartrate', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); // Test lowest smartrate with valid filters const lowestSmartRate = await client.Shipment.lowestSmartRate(shipment.id, 3, 'percentile_90'); @@ -235,7 +258,9 @@ describe('Shipment Service', function () { }); it('raises an error for lowestSmartRate when no rates are found due to deliveryDays', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); // Test lowest smartrate with invalid filters (should error due to strict deliveryDays) try { @@ -248,7 +273,9 @@ describe('Shipment Service', function () { }); it('raises an error for lowestSmartRate when no rates are found due to deliveryAccuracy', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); // Test lowest smartrate with invalid filters (should error due to invalid deliveryAccuracy) try { @@ -262,7 +289,9 @@ describe('Shipment Service', function () { }); it('gets the lowest smartrate from a list of smartRates', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with valid filters @@ -273,7 +302,9 @@ describe('Shipment Service', function () { }); it('raises an error for getLowestSmartRate when no rates are found due to deliveryDays', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with invalid filters (should error due to strict deliveryDays) @@ -283,7 +314,9 @@ describe('Shipment Service', function () { }); it('raises an error for getLowestSmartRate when no rates are found due to deliveryAccuracy', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const smartRates = await client.Shipment.getSmartRates(shipment.id); // Test lowest smartrate with invalid filters (should error due to invalid deliveryAccuracy) @@ -296,7 +329,9 @@ describe('Shipment Service', function () { }); it('generates a form for a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.oneCallBuyShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.oneCallBuyShipment() as ShipmentTestCreateInput, + ); const formType = 'return_packing_slip'; @@ -315,16 +350,22 @@ describe('Shipment Service', function () { }); it('buys a shipment with insuranceAmount', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const boughtShipment = await client.Shipment.buy(shipment.id, shipment.lowestRate(), 100); expect(boughtShipment.insurance).to.equal('100.00'); }); it('buys a shipment with end_shipper_id', async function () { - const endShipper = await client.EndShipper.create(Fixture.caAddress1() as EndShipperTestCreateInput); + const endShipper = await client.EndShipper.create( + Fixture.caAddress1() as EndShipperTestCreateInput, + ); - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const boughtShipment = await client.Shipment.buy( shipment.id, shipment.lowestRate(), @@ -336,7 +377,9 @@ describe('Shipment Service', function () { }); it('retrieve estimated delivery dates for each of the Rates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const estimatedDeliveryDates = await client.Shipment.retrieveEstimatedDeliveryDate( shipment.id, Fixture.plannedShipDate(), @@ -350,7 +393,9 @@ describe('Shipment Service', function () { }); it('retrieve recommended ship dates for each of the Rates of a shipment', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const recommendedShipDates = await client.Shipment.recommendShipDate( shipment.id, Fixture.plannedDeliveryDate(), @@ -375,7 +420,9 @@ describe('Shipment Service', function () { }); it('buys a Shipment with Luma', async function () { - const shipment = await client.Shipment.create(Fixture.basicShipment() as ShipmentTestCreateInput); + const shipment = await client.Shipment.create( + Fixture.basicShipment() as ShipmentTestCreateInput, + ); const boughtShipment = await client.Shipment.buyLuma(shipment.id, { ruleset_name: Fixture.lumaRulesetName(), From 7a7f9a47b7101db94a4bc6530481722e0554373c Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:10:53 -0600 Subject: [PATCH 13/14] Use replay-only Polly mode for existing cassettes --- test/helpers/setup_polly.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/helpers/setup_polly.js b/test/helpers/setup_polly.js index fa454dec8..127e67fac 100644 --- a/test/helpers/setup_polly.js +++ b/test/helpers/setup_polly.js @@ -1,6 +1,7 @@ import FetchAdapter from '@pollyjs/adapter-fetch'; import { Polly } from '@pollyjs/core'; import FSPersister from '@pollyjs/persister-fs'; +import { existsSync } from 'fs'; import { resolve } from 'path'; Polly.register(FSPersister); @@ -168,22 +169,33 @@ function setupLegacyRequestIdentityCompatibility(server) { }); } +function getPollyMode(recordingsDir, recordingName) { + const cassettePath = resolve(recordingsDir, recordingName, 'recording.har'); + + // Source-of-truth behavior: replay when cassette exists, record only when missing. + return existsSync(cassettePath) ? 'replay' : 'record'; +} + // New setup function for Vitest function setupPollyTests() { /** @type {Polly} */ let polly; + const recordingsDir = resolve(__dirname, '../cassettes'); beforeEach((context) => { const suiteName = context.task?.suite?.name || 'unknown-suite'; + const recordingName = `${suiteName}/${context.task.name}`; + const mode = getPollyMode(recordingsDir, recordingName); - polly = new Polly(`${suiteName}/${context.task.name}`, { + polly = new Polly(recordingName, { adapters: ['fetch'], persister: 'fs', - recordIfMissing: true, + mode, + recordIfMissing: false, recordFailedRequests: true, persisterOptions: { fs: { - recordingsDir: resolve(__dirname, '../cassettes'), + recordingsDir, }, }, matchRequestsBy: { From 52aa9b6cb03f82e0c6c0717091e5e6d29dd0fb9a Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:22:30 -0600 Subject: [PATCH 14/14] Use default Polly mode behavior --- test/helpers/setup_polly.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/helpers/setup_polly.js b/test/helpers/setup_polly.js index 127e67fac..bb6dd65e8 100644 --- a/test/helpers/setup_polly.js +++ b/test/helpers/setup_polly.js @@ -1,7 +1,6 @@ import FetchAdapter from '@pollyjs/adapter-fetch'; import { Polly } from '@pollyjs/core'; import FSPersister from '@pollyjs/persister-fs'; -import { existsSync } from 'fs'; import { resolve } from 'path'; Polly.register(FSPersister); @@ -169,13 +168,6 @@ function setupLegacyRequestIdentityCompatibility(server) { }); } -function getPollyMode(recordingsDir, recordingName) { - const cassettePath = resolve(recordingsDir, recordingName, 'recording.har'); - - // Source-of-truth behavior: replay when cassette exists, record only when missing. - return existsSync(cassettePath) ? 'replay' : 'record'; -} - // New setup function for Vitest function setupPollyTests() { /** @type {Polly} */ @@ -185,13 +177,10 @@ function setupPollyTests() { beforeEach((context) => { const suiteName = context.task?.suite?.name || 'unknown-suite'; const recordingName = `${suiteName}/${context.task.name}`; - const mode = getPollyMode(recordingsDir, recordingName); polly = new Polly(recordingName, { adapters: ['fetch'], persister: 'fs', - mode, - recordIfMissing: false, recordFailedRequests: true, persisterOptions: { fs: {