From ec6d821004c0647c4f4840c2167a001ff709f11c Mon Sep 17 00:00:00 2001 From: Luke Winship Date: Wed, 12 Jul 2023 17:30:42 +0100 Subject: [PATCH] feat(criteria): Add TestOpportunityBookable(Non)FreeCancellable criterias --- .../TestOpportunityBookableCancellable.d.ts | 1 + .../criteria/TestOpportunityBookableFree.d.ts | 1 + ...estOpportunityBookableFreeCancellable.d.ts | 4 +++ .../TestOpportunityBookableNonFree.d.ts | 1 + ...OpportunityBookableNonFreeCancellable.d.ts | 4 +++ .../built-types/criteria/criteriaUtils.d.ts | 7 +++++ .../built-types/testDataShape.d.ts | 10 +++++++ .../TestOpportunityBookableCancellable.js | 23 +++++++------- .../criteria/TestOpportunityBookableFree.js | 22 +++++++------- .../TestOpportunityBookableFreeCancellable.js | 30 +++++++++++++++++++ .../TestOpportunityBookableNonFree.js | 18 ++++++----- ...stOpportunityBookableNonFreeCancellable.js | 30 +++++++++++++++++++ .../src/criteria/criteriaUtils.js | 19 ++++++++++++ .../src/criteria/index.js | 4 +++ .../src/testDataShape.js | 13 ++++++++ 15 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFreeCancellable.d.ts create mode 100644 packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFreeCancellable.d.ts create mode 100644 packages/test-interface-criteria/src/criteria/TestOpportunityBookableFreeCancellable.js create mode 100644 packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFreeCancellable.js diff --git a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableCancellable.d.ts b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableCancellable.d.ts index ffc0464942..7c53784a49 100644 --- a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableCancellable.d.ts +++ b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableCancellable.d.ts @@ -3,3 +3,4 @@ export type OfferConstraint = (offer: import("../types/Offer").Offer, opportunit * Implements https://openactive.io/test-interface#TestOpportunityBookableCancellable */ export const TestOpportunityBookableCancellable: import("../types/Criteria").Criteria; +export const mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint: [string, import("../types/Criteria").OfferConstraint]; diff --git a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFree.d.ts b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFree.d.ts index 54831228e0..2622c18e8c 100644 --- a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFree.d.ts +++ b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFree.d.ts @@ -3,3 +3,4 @@ export type OfferConstraint = (offer: import("../types/Offer").Offer, opportunit * Implements https://openactive.io/test-interface#TestOpportunityBookableFree */ export const TestOpportunityBookableFree: import("../types/Criteria").Criteria; +export const onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint: [string, import("../types/Criteria").OfferConstraint]; diff --git a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFreeCancellable.d.ts b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFreeCancellable.d.ts new file mode 100644 index 0000000000..0d5a5d07ef --- /dev/null +++ b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableFreeCancellable.d.ts @@ -0,0 +1,4 @@ +/** + * Implements https://openactive.io/test-interface#TestOpportunityBookableFreeCancellable + */ +export const TestOpportunityBookableFreeCancellable: import("../types/Criteria").Criteria; diff --git a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFree.d.ts b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFree.d.ts index 1cdd4e6f63..ff3f555bd6 100644 --- a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFree.d.ts +++ b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFree.d.ts @@ -3,3 +3,4 @@ export type OfferConstraint = (offer: import("../types/Offer").Offer, opportunit * Implements https://openactive.io/test-interface#TestOpportunityBookableNonFree */ export const TestOpportunityBookableNonFree: import("../types/Criteria").Criteria; +export const onlyNonFreeBookableOfferConstraint: [string, import("../types/Criteria").OfferConstraint]; diff --git a/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFreeCancellable.d.ts b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFreeCancellable.d.ts new file mode 100644 index 0000000000..8bd0583ac5 --- /dev/null +++ b/packages/test-interface-criteria/built-types/criteria/TestOpportunityBookableNonFreeCancellable.d.ts @@ -0,0 +1,4 @@ +/** + * Implements https://openactive.io/test-interface#TestOpportunityBookableNonFreeCancellable + */ +export const TestOpportunityBookableNonFreeCancellable: import("../types/Criteria").Criteria; diff --git a/packages/test-interface-criteria/built-types/criteria/criteriaUtils.d.ts b/packages/test-interface-criteria/built-types/criteria/criteriaUtils.d.ts index 5aa7b7bcc0..3df8bb4b6d 100644 --- a/packages/test-interface-criteria/built-types/criteria/criteriaUtils.d.ts +++ b/packages/test-interface-criteria/built-types/criteria/criteriaUtils.d.ts @@ -133,6 +133,7 @@ export function mustNotAllowFullRefund(offer: import("../types/Offer").Offer, op * @type {OfferConstraint} */ export function mustAllowFullRefund(offer: import("../types/Offer").Offer): boolean; +export const mustAllowFullRefundOfferConstraint: [string, import("../types/Criteria").OfferConstraint]; /** * @type {OfferConstraint} */ @@ -158,3 +159,9 @@ export function excludePaidBookableOffersWithPrepaymentUnavailable(offer: import * @return {TestDataShape} */ export function extendTestDataShape(baseTestDataShape: TestDataShape, extraTestDataShape: TestDataShape, criteriaName: string): TestDataShape; +/** + * @param {string} name + * @param {OfferConstraint} constraint + * @returns {Criteria['offerConstraints'][number]} + */ +export function createCriteriaOfferConstraint(name: string, constraint: OfferConstraint): Criteria['offerConstraints'][number]; diff --git a/packages/test-interface-criteria/built-types/testDataShape.d.ts b/packages/test-interface-criteria/built-types/testDataShape.d.ts index 8b2901467b..7fc63e9e61 100644 --- a/packages/test-interface-criteria/built-types/testDataShape.d.ts +++ b/packages/test-interface-criteria/built-types/testDataShape.d.ts @@ -114,4 +114,14 @@ export namespace shapeConstraintRecipes { export function mustAllowFullRefund(): { 'oa:allowCustomerCancellationFullRefund': import("./types/TestDataShape").BooleanNodeConstraint; }; + export function mustBeWithinCancellationWindowOrHaveNoWindow(): { + 'oa:latestCancellationBeforeStartDate': import("./types/TestDataShape").NullNodeConstraint; + }; + export function onlyNonFreeBookableOffers(): { + 'schema:price': import("./types/TestDataShape").NumericNodeConstraint; + }; + export function onlyFreeBookableOffersWithUnavailablePrepayment(): { + 'schema:price': import("./types/TestDataShape").NumericNodeConstraint; + 'oa:openBookingPrepayment': import("./types/TestDataShape").OptionNodeConstraint; + }; } diff --git a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableCancellable.js b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableCancellable.js index ff0eb89aa5..c28714ced6 100644 --- a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableCancellable.js +++ b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableCancellable.js @@ -1,6 +1,6 @@ const { TestOpportunityBookable } = require('./TestOpportunityBookable'); -const { createCriteria, mustAllowFullRefund, getDateBeforeWhichCancellationsCanBeMade } = require('./criteriaUtils'); -const { BLOCKED_FIELD, shapeConstraintRecipes } = require('../testDataShape'); +const { createCriteria, getDateBeforeWhichCancellationsCanBeMade, createCriteriaOfferConstraint, mustAllowFullRefundOfferConstraint } = require('./criteriaUtils'); +const { shapeConstraintRecipes } = require('../testDataShape'); /** * @typedef {import('../types/Criteria').OfferConstraint} OfferConstraint @@ -17,6 +17,11 @@ function mustBeWithinCancellationWindowOrHaveNoWindow(offer, opportunity, option return options.harvestStartTimeTwoHoursLater < dateBeforeWhichCancellationsCanBeMade; } +const mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint = createCriteriaOfferConstraint( + 'Offer must not have cancellation window (`latestCancellationBeforeStartDate`), or be within the cancellation window', + mustBeWithinCancellationWindowOrHaveNoWindow, +); + /** * Implements https://openactive.io/test-interface#TestOpportunityBookableCancellable */ @@ -24,20 +29,13 @@ const TestOpportunityBookableCancellable = createCriteria({ name: 'TestOpportunityBookableCancellable', opportunityConstraints: [], offerConstraints: [ - [ - 'Offer must not have cancellation window (`latestCancellationBeforeStartDate`), or be within the cancellation window', - mustBeWithinCancellationWindowOrHaveNoWindow, - ], - [ - 'Offer must be fully refundable on customer cancellation, with `"allowCustomerCancellationFullRefund": true`', - mustAllowFullRefund, - ], + mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint, + mustAllowFullRefundOfferConstraint, ], testDataShape: () => ({ offerConstraints: { ...shapeConstraintRecipes.mustAllowFullRefund(), - // mustBeWithinCancellationWindowOrHaveNoWindow - 'oa:latestCancellationBeforeStartDate': BLOCKED_FIELD, + ...shapeConstraintRecipes.mustBeWithinCancellationWindowOrHaveNoWindow(), }, }), includeConstraintsFromCriteria: TestOpportunityBookable, @@ -45,4 +43,5 @@ const TestOpportunityBookableCancellable = createCriteria({ module.exports = { TestOpportunityBookableCancellable, + mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint, }; diff --git a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFree.js b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFree.js index ee8a05ae1e..24cd13ad25 100644 --- a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFree.js +++ b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFree.js @@ -1,6 +1,6 @@ const { TestOpportunityBookable } = require('./TestOpportunityBookable'); -const { createCriteria } = require('./criteriaUtils'); -const { prepaymentOptionNodeConstraint, FREE_PRICE_QUANTITATIVE_VALUE } = require('../testDataShape'); +const { createCriteria, createCriteriaOfferConstraint } = require('./criteriaUtils'); +const { shapeConstraintRecipes } = require('../testDataShape'); /** * @typedef {import('../types/Criteria').OfferConstraint} OfferConstraint @@ -13,6 +13,11 @@ function onlyFreeBookableOffersWithUnavailablePrepayment(offer) { return offer.price === 0 && (!offer.openBookingPrepayment || offer.openBookingPrepayment === 'https://openactive.io/Unavailable'); } +const onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint = createCriteriaOfferConstraint( + 'Only free bookable Offers (free offers must always either omit `openBookingPrepayment` or set it to `https://openactive.io/Unavailable`) ', + onlyFreeBookableOffersWithUnavailablePrepayment, +); + /** * Implements https://openactive.io/test-interface#TestOpportunityBookableFree */ @@ -20,19 +25,11 @@ const TestOpportunityBookableFree = createCriteria({ name: 'TestOpportunityBookableFree', opportunityConstraints: [], offerConstraints: [ - [ - 'Only free bookable Offers (free offers must always either omit `openBookingPrepayment` or set it to `https://openactive.io/Unavailable`) ', - onlyFreeBookableOffersWithUnavailablePrepayment, - ], + onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint, ], testDataShape: () => ({ offerConstraints: { - // onlyFreeBookableOffersWithUnavailablePrepayment - 'schema:price': FREE_PRICE_QUANTITATIVE_VALUE, - 'oa:openBookingPrepayment': prepaymentOptionNodeConstraint({ - allowlist: ['https://openactive.io/Unavailable'], - allowNull: true, - }), + ...shapeConstraintRecipes.onlyFreeBookableOffersWithUnavailablePrepayment(), }, }), includeConstraintsFromCriteria: TestOpportunityBookable, @@ -40,4 +37,5 @@ const TestOpportunityBookableFree = createCriteria({ module.exports = { TestOpportunityBookableFree, + onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint, }; diff --git a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFreeCancellable.js b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFreeCancellable.js new file mode 100644 index 0000000000..f97ba17be7 --- /dev/null +++ b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableFreeCancellable.js @@ -0,0 +1,30 @@ +const { shapeConstraintRecipes } = require('../testDataShape'); +const { TestOpportunityBookable } = require('./TestOpportunityBookable'); +const { mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint } = require('./TestOpportunityBookableCancellable'); +const { onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint } = require('./TestOpportunityBookableFree'); +const { createCriteria, mustAllowFullRefundOfferConstraint } = require('./criteriaUtils'); + +/** + * Implements https://openactive.io/test-interface#TestOpportunityBookableFreeCancellable + */ +const TestOpportunityBookableFreeCancellable = createCriteria({ + name: 'TestOpportunityBookableFreeCancellable', + opportunityConstraints: [], + offerConstraints: [ + onlyFreeBookableOffersWithUnavailablePrepaymentOfferConstraint, + mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint, + mustAllowFullRefundOfferConstraint, + ], + testDataShape: () => ({ + offerConstraints: { + ...shapeConstraintRecipes.onlyFreeBookableOffersWithUnavailablePrepayment(), + ...shapeConstraintRecipes.mustAllowFullRefund(), + ...shapeConstraintRecipes.mustBeWithinCancellationWindowOrHaveNoWindow(), + }, + }), + includeConstraintsFromCriteria: TestOpportunityBookable, +}); + +module.exports = { + TestOpportunityBookableFreeCancellable, +}; diff --git a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFree.js b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFree.js index ae8458c2bf..9f04a77841 100644 --- a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFree.js +++ b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFree.js @@ -1,6 +1,6 @@ const { TestOpportunityBookable } = require('./TestOpportunityBookable'); -const { createCriteria } = require('./criteriaUtils'); -const { NON_FREE_PRICE_QUANTITATIVE_VALUE } = require('../testDataShape'); +const { createCriteria, createCriteriaOfferConstraint } = require('./criteriaUtils'); +const { shapeConstraintRecipes } = require('../testDataShape'); /** * @typedef {import('../types/Criteria').OfferConstraint} OfferConstraint @@ -13,6 +13,11 @@ function onlyNonFreeBookableOffers(offer) { return offer.price > 0; } +const onlyNonFreeBookableOfferConstraint = createCriteriaOfferConstraint( + 'Only non-free bookable Offers', + onlyNonFreeBookableOffers, +); + /** * Implements https://openactive.io/test-interface#TestOpportunityBookableNonFree */ @@ -20,15 +25,11 @@ const TestOpportunityBookableNonFree = createCriteria({ name: 'TestOpportunityBookableNonFree', opportunityConstraints: [], offerConstraints: [ - [ - 'Only non-free bookable Offers', - onlyNonFreeBookableOffers, - ], + onlyNonFreeBookableOfferConstraint, ], testDataShape: () => ({ offerConstraints: { - // onlyNonFreeBookableOffers - 'schema:price': NON_FREE_PRICE_QUANTITATIVE_VALUE, + ...shapeConstraintRecipes.onlyNonFreeBookableOffers(), }, }), includeConstraintsFromCriteria: TestOpportunityBookable, @@ -36,4 +37,5 @@ const TestOpportunityBookableNonFree = createCriteria({ module.exports = { TestOpportunityBookableNonFree, + onlyNonFreeBookableOfferConstraint, }; diff --git a/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFreeCancellable.js b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFreeCancellable.js new file mode 100644 index 0000000000..379f3443be --- /dev/null +++ b/packages/test-interface-criteria/src/criteria/TestOpportunityBookableNonFreeCancellable.js @@ -0,0 +1,30 @@ +const { shapeConstraintRecipes } = require('../testDataShape'); +const { TestOpportunityBookable } = require('./TestOpportunityBookable'); +const { mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint } = require('./TestOpportunityBookableCancellable'); +const { onlyNonFreeBookableOfferConstraint } = require('./TestOpportunityBookableNonFree'); +const { createCriteria, mustAllowFullRefundOfferConstraint } = require('./criteriaUtils'); + +/** + * Implements https://openactive.io/test-interface#TestOpportunityBookableNonFreeCancellable + */ +const TestOpportunityBookableNonFreeCancellable = createCriteria({ + name: 'TestOpportunityBookableNonFreeCancellable', + opportunityConstraints: [], + offerConstraints: [ + onlyNonFreeBookableOfferConstraint, + mustBeWithinCancellationWindowOrHaveNoWindowOfferConstraint, + mustAllowFullRefundOfferConstraint, + ], + testDataShape: () => ({ + offerConstraints: { + ...shapeConstraintRecipes.onlyNonFreeBookableOffers(), + ...shapeConstraintRecipes.mustAllowFullRefund(), + ...shapeConstraintRecipes.mustBeWithinCancellationWindowOrHaveNoWindow(), + }, + }), + includeConstraintsFromCriteria: TestOpportunityBookable, +}); + +module.exports = { + TestOpportunityBookableNonFreeCancellable, +}; diff --git a/packages/test-interface-criteria/src/criteria/criteriaUtils.js b/packages/test-interface-criteria/src/criteria/criteriaUtils.js index d0656a1b61..bda38a310c 100644 --- a/packages/test-interface-criteria/src/criteria/criteriaUtils.js +++ b/packages/test-interface-criteria/src/criteria/criteriaUtils.js @@ -395,6 +395,11 @@ function mustAllowFullRefund(offer) { return offer.allowCustomerCancellationFullRefund === true; } +const mustAllowFullRefundOfferConstraint = createCriteriaOfferConstraint( + 'Offer must be fully refundable on customer cancellation, with `"allowCustomerCancellationFullRefund": true`', + mustAllowFullRefund, +); + /** * @type {OfferConstraint} */ @@ -453,6 +458,18 @@ function excludePaidBookableOffersWithPrepaymentUnavailable(offer) { return !(offer.price > 0 && offer.openBookingPrepayment === 'https://openactive.io/Unavailable'); } +/** + * @param {string} name + * @param {OfferConstraint} constraint + * @returns {Criteria['offerConstraints'][number]} + */ +function createCriteriaOfferConstraint(name, constraint) { + // It's frozen so that it may be easily used in multiple criteria without the possibility of + // erroneous mutation in one criteria affecting all others. + // In a later version of TS, just use `const` rather than `any` + return Object.freeze(/** @type {any} */([name, constraint])); +} + module.exports = { createCriteria, getId, @@ -474,9 +491,11 @@ module.exports = { mustBeOutsideCancellationWindow, mustNotAllowFullRefund, mustAllowFullRefund, + mustAllowFullRefundOfferConstraint, mustRequireAdditionalDetails, mustNotRequireAdditionalDetails, sellerMustAllowOpenBooking, excludePaidBookableOffersWithPrepaymentUnavailable, extendTestDataShape, + createCriteriaOfferConstraint, }; diff --git a/packages/test-interface-criteria/src/criteria/index.js b/packages/test-interface-criteria/src/criteria/index.js index 4f2cf02bfb..74c801c9f6 100644 --- a/packages/test-interface-criteria/src/criteria/index.js +++ b/packages/test-interface-criteria/src/criteria/index.js @@ -26,6 +26,8 @@ const { TestOpportunityBookableSellerTermsOfService } = require('./TestOpportuni const { TestOpportunityOnlineBookable } = require('./TestOpportunityOnlineBookable'); const { TestOpportunityOfflineBookable } = require('./TestOpportunityOfflineBookable'); const { TestOpportunityBookableWithNegotiation } = require('./TestOpportunityBookableWithNegotiation'); +const { TestOpportunityBookableFreeCancellable } = require('./TestOpportunityBookableFreeCancellable'); +const { TestOpportunityBookableNonFreeCancellable } = require('./TestOpportunityBookableNonFreeCancellable'); module.exports = { allCriteria: [ @@ -39,6 +41,8 @@ module.exports = { TestOpportunityBookableWithinValidFromBeforeStartDate, TestOpportunityBookableCancellable, TestOpportunityBookableNotCancellable, + TestOpportunityBookableFreeCancellable, + TestOpportunityBookableNonFreeCancellable, TestOpportunityBookableInPast, TestOpportunityBookableOutsideValidFromBeforeStartDate, TestOpportunityBookableCancellableNoWindow, diff --git a/packages/test-interface-criteria/src/testDataShape.js b/packages/test-interface-criteria/src/testDataShape.js index 5b6eba5c35..779e670092 100644 --- a/packages/test-interface-criteria/src/testDataShape.js +++ b/packages/test-interface-criteria/src/testDataShape.js @@ -209,6 +209,19 @@ const shapeConstraintRecipes = { mustAllowFullRefund: () => ({ 'oa:allowCustomerCancellationFullRefund': TRUE_BOOLEAN_CONSTRAINT, }), + mustBeWithinCancellationWindowOrHaveNoWindow: () => ({ + 'oa:latestCancellationBeforeStartDate': BLOCKED_FIELD, + }), + onlyNonFreeBookableOffers: () => ({ + 'schema:price': NON_FREE_PRICE_QUANTITATIVE_VALUE, + }), + onlyFreeBookableOffersWithUnavailablePrepayment: () => ({ + 'schema:price': FREE_PRICE_QUANTITATIVE_VALUE, + 'oa:openBookingPrepayment': prepaymentOptionNodeConstraint({ + allowlist: ['https://openactive.io/Unavailable'], + allowNull: true, + }), + }), }; module.exports = {