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/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.js b/src/services/address_service.ts similarity index 76% rename from src/services/address_service.js rename to src/services/address_service.ts index 7d17e7425..c16e6326a 100644 --- a/src/services/address_service.js +++ b/src/services/address_service.ts @@ -1,5 +1,26 @@ import baseService from './base_service'; +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 | Array | null; + verify_strict?: boolean | string | Array | null; + verify_carrier?: string | null; +}; + +type PaginationCollection = Record; + export default (easypostClient) => /** * The AddressService class provides methods for interacting with EasyPost {@link Address} objects. @@ -12,10 +33,10 @@ 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: AddressCreateParameters): Promise { const url = 'addresses'; - const wrappedParams = {}; + const wrappedParams: Record = {}; if (params.verify) { wrappedParams.verify = params.verify; @@ -43,10 +64,10 @@ 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: AddressCreateParameters): Promise { const url = `addresses/create_and_verify`; - const wrappedParams = {}; + const wrappedParams: Record = {}; if (params.verify_carrier) { wrappedParams.verify_carrier = params.verify_carrier; @@ -70,7 +91,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 +103,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, pageSize = null) { + static async getNextPage(addresses: PaginationCollection, pageSize?: number): Promise { const url = 'addresses'; return this._getNextPage(url, 'addresses', addresses, pageSize); } @@ -93,7 +114,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,12 +126,12 @@ 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); - return this._convertToEasyPostObject(response.body.address); + return this._convertToEasyPostObject(response.body.address, {}); } catch (e) { return Promise.reject(e); } diff --git a/src/services/base_service.ts b/src/services/base_service.ts index 10cd2d829..aeda360c9 100644 --- a/src/services/base_service.ts +++ b/src/services/base_service.ts @@ -207,7 +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, params = {}) { + static _convertToEasyPostObject(response: any, params: any = {}): any { const modelResponse = this._buildEasyPostObject(response, params); return this._toPlainEasyPostObject(modelResponse); @@ -274,7 +274,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: any, + pageSize: number | null = null, + optionalParams: any = {}, + ): 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.js b/src/services/customs_info_service.ts similarity index 65% rename from src/services/customs_info_service.js rename to src/services/customs_info_service.ts index c781bfbc7..ce450138c 100644 --- a/src/services/customs_info_service.js +++ b/src/services/customs_info_service.ts @@ -1,5 +1,20 @@ import baseService from './base_service'; +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) => /** * The CustomsInfoService class provides methods for interacting with EasyPost {@link CustomsInfo} objects. @@ -12,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) { + static async create(params: CustomsInfoCreateParameters): Promise { const url = 'customs_infos'; const wrappedParams = { @@ -28,7 +43,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.js b/src/services/customs_item_service.ts similarity index 75% rename from src/services/customs_item_service.js rename to src/services/customs_item_service.ts index aca0f97f5..7d6c69ca1 100644 --- a/src/services/customs_item_service.js +++ b/src/services/customs_item_service.ts @@ -1,5 +1,16 @@ import baseService from './base_service'; +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) => /** * The CustomsItemService class provides methods for interacting with EasyPost {@link CustomsItem} objects. @@ -12,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) { + static async create(params: CustomsItemCreateParameters): Promise { const url = 'customs_items'; const wrappedParams = { @@ -28,7 +39,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.js b/src/services/parcel_service.ts similarity index 78% rename from src/services/parcel_service.js rename to src/services/parcel_service.ts index c12c4f3d9..4945871c2 100644 --- a/src/services/parcel_service.js +++ b/src/services/parcel_service.ts @@ -1,5 +1,13 @@ import baseService from './base_service'; +type ParcelCreateParameters = Record & { + length?: number | null; + width?: number | null; + height?: number | null; + weight?: number | null; + predefined_package?: string | null; +}; + export default (easypostClient) => /** * The ParcelService class provides methods for interacting with EasyPost {@link Parcel} objects. @@ -12,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) { + static async create(params: ParcelCreateParameters): Promise { const url = 'parcels'; const wrappedParams = { @@ -28,7 +36,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.js b/src/services/shipment_service.ts similarity index 77% rename from src/services/shipment_service.js rename to src/services/shipment_service.ts index f04f2bc7a..c7cb70d24 100644 --- a/src/services/shipment_service.js +++ b/src/services/shipment_service.ts @@ -1,6 +1,46 @@ import Constants from '../constants'; import baseService from './base_service'; +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 | Record[] | null; + tax_identifiers?: Array | null; + options?: Record | null; + line_items?: ShipmentLineItem[] | null; +}; +type ShipmentRateInput = string | { id: string }; +type ShipmentCollection = Record; + export default (easypostClient) => /** * The ShipmentService class provides methods for interacting with EasyPost {@link Shipment} objects. @@ -8,12 +48,12 @@ 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. */ - static async create(params) { + static async create(params: ShipmentCreateParameters): Promise { const url = 'shipments'; const wrappedParams = { @@ -32,7 +72,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') { @@ -41,7 +86,7 @@ export default (easypostClient) => const url = `shipments/${id}/buy`; - const wrappedParams = { + const wrappedParams: Record = { rate: { id: rateId, }, @@ -71,7 +116,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 +135,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,13 +154,13 @@ 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 { const response = await easypostClient._get(url); - return this._convertToEasyPostObject(response.body.result); + return this._convertToEasyPostObject(response.body.result, {}); } catch (e) { return Promise.reject(e); } @@ -128,7 +173,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 +194,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,13 +222,13 @@ 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 { const response = await easypostClient._post(url); - return this._convertToEasyPostObject(response.body); + return this._convertToEasyPostObject(response.body, {}); } catch (e) { return Promise.reject(e); } @@ -192,8 +241,12 @@ 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) { - const smartRates = await this.getSmartRates(id); + static async lowestSmartRate( + id: string, + deliveryDays: number, + deliveryAccuracy: string, + ): Promise { + const smartRates = (await this.getSmartRates(id)) as any[]; return Constants.Utils.getLowestSmartRate( smartRates, deliveryDays, @@ -207,7 +260,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 +272,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, pageSize = null) { + static async getNextPage(shipments: ShipmentCollection, pageSize?: number): Promise { const url = 'shipments'; return this._getNextPage(url, 'shipments', shipments, pageSize); @@ -231,7 +284,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 +296,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 +321,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 +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) { + static async createAndBuyLuma(params: ShipmentCreateParameters): Promise { const url = `shipments/luma`; const wrappedParams = { @@ -308,7 +364,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 { 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 87% rename from test/services/address.test.js rename to test/services/address.test.ts index 536fd8f70..d7ded88fc 100644 --- a/test/services/address.test.js +++ b/test/services/address.test.ts @@ -1,13 +1,17 @@ -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]; +type AddressTestCreateAndVerifyInput = Parameters['createAndVerify']>[0]; + /* eslint-disable func-names */ describe('Address Service', function () { const getPolly = setupPolly.setupPollyTests(); @@ -23,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_/); @@ -31,7 +35,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); @@ -63,7 +67,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 +78,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); @@ -91,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); @@ -127,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); @@ -137,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) => @@ -146,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); @@ -163,7 +167,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 +180,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 AddressTestCreateAndVerifyInput; 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 67% rename from test/services/customs_info.test.js rename to test/services/customs_info.test.ts index 7941ade71..48a882a59 100644 --- a/test/services/customs_info.test.js +++ b/test/services/customs_info.test.ts @@ -1,12 +1,15 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +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.js b/test/services/customs_item.test.ts similarity index 67% rename from test/services/customs_item.test.js rename to test/services/customs_item.test.ts index 14838cd2b..8957693d4 100644 --- a/test/services/customs_item.test.js +++ b/test/services/customs_item.test.ts @@ -1,12 +1,15 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +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.js b/test/services/parcel.test.ts similarity index 68% rename from test/services/parcel.test.js rename to test/services/parcel.test.ts index 4746ce775..a89f20e27 100644 --- a/test/services/parcel.test.js +++ b/test/services/parcel.test.ts @@ -1,12 +1,15 @@ /* eslint-disable func-names */ -import { expect } from 'chai'; +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.js b/test/services/shipment.test.ts similarity index 85% rename from test/services/shipment.test.js rename to test/services/shipment.test.ts index 63f5593c3..09353d3c3 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'; @@ -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 { @@ -231,12 +243,12 @@ 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.'); } }); 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 { @@ -245,12 +257,12 @@ 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); } }); 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(), 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/**"] 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"] +}