Skip to content

Commit

Permalink
fix: handle cutoff times properly
Browse files Browse the repository at this point in the history
- set currentCarrier on initializing configBus
- remove unused settings (priceSaturdayDelivery and priceMondayDelivery)
- add test that runs daily against the real api
- fix a few delivery options tests that marked the wrong results as correct
  • Loading branch information
EdieLemoine committed Jul 7, 2021
1 parent e107b99 commit 0e50559
Show file tree
Hide file tree
Showing 21 changed files with 413 additions and 126 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/live-api-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Run test on the live API

on:
schedule:
- cron: '0 12 * * *'

jobs:
test:
name: Run test on live API
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
ref: 'master'
- uses: bahmutov/npm-install@v1
- name: Run jest
run: ./node_modules/.bin/cross-env npm test -- tests/unit/delivery-options/testLiveApi.spec.js --coverage=false
env:
NODE_ICU_DATA: node_modules/full-icu
28 changes: 12 additions & 16 deletions src/config/extraDeliveryConfig.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import * as CONFIG from '@/data/keys/configKeys';
import { MYPARCEL, SENDMYPARCEL } from '@/data/keys/platformKeys';
import { FEATURES_MONDAY_DELIVERY, FEATURES_SATURDAY_DELIVERY } from '@/data/carrierFeatures';

const MONDAY = 1;
const FRIDAY = 5;
const SATURDAY = 6;
export const MONDAY = 1;
export const TUESDAY = 2;
export const WEDNESDAY = 3;
export const THURSDAY = 4;
export const FRIDAY = 5;
export const SATURDAY = 6;
export const SUNDAY = 0;

/**
* Settings for extra delivery days.
Expand All @@ -12,23 +16,15 @@ const SATURDAY = 6;
*/
export const extraDeliveryConfig = [
{
cutoffTime: CONFIG.SATURDAY_CUTOFF_TIME,
deliveryDay: MONDAY,
dropOffDay: SATURDAY,
platforms: [MYPARCEL],
requires: [
CONFIG.ALLOW_MONDAY_DELIVERY,
CONFIG.SATURDAY_CUTOFF_TIME,
],
cutoffTime: CONFIG.SATURDAY_CUTOFF_TIME,
requires: FEATURES_MONDAY_DELIVERY,
},
{
cutoffTime: CONFIG.FRIDAY_CUTOFF_TIME,
deliveryDay: SATURDAY,
dropOffDay: FRIDAY,
platforms: [SENDMYPARCEL],
requires: [
CONFIG.ALLOW_SATURDAY_DELIVERY,
CONFIG.FRIDAY_CUTOFF_TIME,
],
cutoffTime: CONFIG.FRIDAY_CUTOFF_TIME,
requires: FEATURES_SATURDAY_DELIVERY,
},
];
2 changes: 0 additions & 2 deletions src/data/carrierFeatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export const FEATURES_EVENING_DELIVERY = [
*/
export const FEATURES_MONDAY_DELIVERY = [
CONFIG.ALLOW_MONDAY_DELIVERY,
CONFIG.PRICE_MONDAY_DELIVERY,
CONFIG.SATURDAY_CUTOFF_TIME,
];

Expand Down Expand Up @@ -86,7 +85,6 @@ export const FEATURES_PICKUP = [
*/
export const FEATURES_SATURDAY_DELIVERY = [
CONFIG.ALLOW_SATURDAY_DELIVERY,
CONFIG.PRICE_SATURDAY_DELIVERY,
CONFIG.FRIDAY_CUTOFF_TIME,
];

Expand Down
2 changes: 0 additions & 2 deletions src/data/keys/configKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ export const PRICE_PACKAGE_TYPE_MAILBOX = 'pricePackageTypeMailbox';
* For use with Monday delivery.
*/
export const ALLOW_MONDAY_DELIVERY = 'allowMondayDelivery';
export const PRICE_MONDAY_DELIVERY = 'priceMondayDelivery';
export const SATURDAY_CUTOFF_TIME = 'saturdayCutoffTime';

/*
* For use with Saturday delivery.
*/
export const ALLOW_SATURDAY_DELIVERY = 'allowSaturdayDelivery';
export const FRIDAY_CUTOFF_TIME = 'fridayCutoffTime';
export const PRICE_SATURDAY_DELIVERY = 'priceSaturdayDelivery';

/*
* Carrier settings object
Expand Down
3 changes: 1 addition & 2 deletions src/data/locales/be/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as CONFIG from '@/data/keys/configKeys';
import { DEFAULT_MAX_PAGE_ITEMS, DEFAULT_PRICE } from '@/data/keys/settingsConsts';
import { DEFAULT_MAX_PAGE_ITEMS } from '@/data/keys/settingsConsts';

export const config = {
[CONFIG.LOCALE]: 'nl-BE',

[CONFIG.ALLOW_SATURDAY_DELIVERY]: true,
[CONFIG.PRICE_SATURDAY_DELIVERY]: DEFAULT_PRICE,
[CONFIG.FRIDAY_CUTOFF_TIME]: '15:00',

/**
Expand Down
1 change: 1 addition & 0 deletions src/delivery-options/config/configBus.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export const createConfigBus = (eventCallee = null) => {
this[item] = configuration[item];
});

this.currentCarrier = Object.keys(configuration.config.carrierSettings)[0];
this.weekdays = getWeekdays(configuration.config.locale);

document.dispatchEvent(new Event(EVENTS.UPDATE_DELIVERY_OPTIONS));
Expand Down
16 changes: 16 additions & 0 deletions src/delivery-options/data/request/checkIsDropOffDay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as CONFIG from '@/data/keys/configKeys';
import { configBus as realConfigBus } from '@/delivery-options/config/configBus';

/**
* @param {Number} dropOffDay
* @param {import('@/delivery-options/config/configBus').configBus} configBus
* @param {Number} day
*
* @returns {Boolean}
*/
export function checkIsDropOffDay(dropOffDay, configBus = realConfigBus, day = new Date().getDay()) {
const dateMatches = day === dropOffDay;
const dateIsDropOffDay = configBus.get(CONFIG.DROP_OFF_DAYS).includes(day);

return dateMatches && dateIsDropOffDay;
}
21 changes: 7 additions & 14 deletions src/delivery-options/data/request/getCutOffTime.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import * as CONFIG from '@/data/keys/configKeys';
import { extraDeliveryConfig } from '@/config/extraDeliveryConfig';
import { checkIsDropOffDay } from '@/delivery-options/data/request/checkIsDropOffDay';
import { getExtraDropOffDay } from '@/delivery-options/data/request/getExtraDropOffDay';
import { configBus as realConfigBus } from '@/delivery-options/config/configBus';

/**
* Get cutoff time for a special delivery day. Returns default cutoff time if the conditions for an extra delivery day
* don't pass.
*
* @param {import('@/delivery-options/config/configBus')} configBus - Optional parameter for easier testing.
* @param {import('@/delivery-options/config/configBus').configBus} configBus - Optional parameter for easier testing.
*
* @returns {MyParcelDeliveryOptions.Config.cutoffTime}
* @returns {String}
*/
export function getCutOffTime(configBus = realConfigBus) {
const today = new Date().getDay();
const extraDropOffDay = getExtraDropOffDay(configBus);
const todayIsExtraDropOffDay = extraDropOffDay && checkIsDropOffDay(extraDropOffDay.dropOffDay, configBus);

const extraDropOffDay = extraDeliveryConfig.find((setting) => {
const allowedForPlatform = setting.platforms.includes(configBus.get(CONFIG.PLATFORM));
const todayIsExtraDay = today === setting.dropOffDay;
const requirementsFulfilled = setting.requires.every((requirement) => Boolean(configBus.get(requirement)));
const extraDayIsDropOffDay = configBus.get(CONFIG.DROP_OFF_DAYS).includes(setting.dropOffDay);

return todayIsExtraDay && allowedForPlatform && extraDayIsDropOffDay && requirementsFulfilled;
});

return extraDropOffDay
return todayIsExtraDropOffDay
? configBus.get(extraDropOffDay.cutoffTime)
: configBus.get(CONFIG.CUTOFF_TIME);
}
21 changes: 21 additions & 0 deletions src/delivery-options/data/request/getExtraDropOffDay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as CONFIG from '@/data/keys/configKeys';
import { CarrierConfigurationFactory } from '@/data/carriers/carrierConfigurationFactory';
import { extraDeliveryConfig } from '@/config/extraDeliveryConfig';
import { configBus as realConfigBus } from '@/delivery-options/config/configBus';

/**
* @param {import('@/delivery-options/config/configBus').configBus} configBus
*
* @returns {Object}
*/
export function getExtraDropOffDay(configBus = realConfigBus) {
const platform = configBus.get(CONFIG.PLATFORM);
const carrierConfiguration = CarrierConfigurationFactory.create(configBus.currentCarrier, platform);

return extraDeliveryConfig.find((setting) => {
const allowedForCarrierAndPlatform = carrierConfiguration.hasFeature(setting.requires);
const requiredOptionsPresent = setting.requires.every(configBus.get);

return allowedForCarrierAndPlatform && requiredOptionsPresent;
});
}
4 changes: 0 additions & 4 deletions src/sandbox/translations/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ export const englishTranslations = {
[`field.${CONFIG.PICKUP_LOCATIONS_MAP_TILE_LAYER_DATA}`]: 'Map tile layer data',
[`field.${CONFIG.PRICE_EVENING_DELIVERY}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_EVENING_DELIVERY}`]: 'Evening delivery price',
[`field.${CONFIG.PRICE_MONDAY_DELIVERY}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_MONDAY_DELIVERY}`]: 'Monday delivery price',
[`field.${CONFIG.PRICE_MORNING_DELIVERY}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_MORNING_DELIVERY}`]: 'Morning delivery price',
[`field.${CONFIG.PRICE_ONLY_RECIPIENT}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
Expand All @@ -83,8 +81,6 @@ export const englishTranslations = {
[`field.${CONFIG.PRICE_PICKUP}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_PICKUP}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_PICKUP}`]: 'Pickup price',
[`field.${CONFIG.PRICE_SATURDAY_DELIVERY}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_SATURDAY_DELIVERY}`]: 'Saturday delivery price',
[`field.${CONFIG.PRICE_SIGNATURE}.description`]: `@:field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`,
[`field.${CONFIG.PRICE_SIGNATURE}`]: 'Signature price',
[`field.${CONFIG.PRICE_STANDARD_DELIVERY}.description`]: 'The amount of money you charge for this feature. If the amount is negative the price will appear green in the delivery options to show it\'s a discount.',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createCutOffTimeDate } from '@Tests/helpers/createCutOffTimeDate';

/**
* @param {String} cutoffTime - Timestamp in HH:mm format.
* @param {import('dayjs').Dayjs} date
*
* @returns {import('dayjs').Dayjs}
*/
export function cutoffTimeHasPassed(cutoffTime, date) {
const cutOffTimeDate = createCutOffTimeDate(cutoffTime, date);

return !date.isBefore(cutOffTimeDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CarrierConfigurationFactory } from '@/data/carriers/carrierConfigurationFactory';
import { extraDeliveryConfig } from '@/config/extraDeliveryConfig';
import { platformCarrierMap } from '@/config/platform/platformCarrierMap';

/**
* Use passed args to find a valid extra delivery day.
*
* @param {Object} args
* @param {Number} dayOfWeek
*
* @returns {Object}
*/
export function findExtraDelivery(args, dayOfWeek) {
// Falls back to the first carrier for current platform.
const carrier = args.carrier ?? platformCarrierMap[args.platform][0];
const carrierConfiguration = CarrierConfigurationFactory.create(carrier, args.platform);

return extraDeliveryConfig.find((setting) => {
const isToday = setting.deliveryDay === dayOfWeek;
const hasDropOffDay = args.dropoff_days.includes(setting.dropOffDay);
const allowedForCarrierAndPlatform = carrierConfiguration.hasFeature(setting.requires);

return isToday && hasDropOffDay && allowedForCarrierAndPlatform;
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Find the last possible dropoff day for a given delivery day.
*
* @param {import('dayjs').Dayjs} deliveryDay
* @param {String} dropOffDays
* @returns {import('dayjs').Dayjs}
*/
export function getDropOffDay(deliveryDay, dropOffDays) {
const dropOffDaysArray = dropOffDays.split(';').map(Number);

let dropOffDay = deliveryDay.subtract(1, 'day');

while (!dropOffDaysArray.includes(dropOffDay.weekday())) {
dropOffDay = dropOffDay.subtract(1, 'day');
}

return dropOffDay;
}
Original file line number Diff line number Diff line change
@@ -1,59 +1,54 @@
import { MONDAY, SATURDAY, SUNDAY } from '@/config/extraDeliveryConfig';
import { MYPARCEL, SENDMYPARCEL } from '@/data/keys/platformKeys';
import { cutoffTimeHasPassed } from '@Mocks/@myparcel/js-sdk/dist/data/delivery-options/cutoffTimeHasPassed';
import { dayjs } from '@Tests/dayjs';
import { extraDeliveryConfig } from '@/config/extraDeliveryConfig';
import { findExtraDelivery } from '@Mocks/@myparcel/js-sdk/dist/data/delivery-options/findExtraDelivery';
import { getDeliveryOptionsEntry } from './getDeliveryOptionsEntry';
import { getDropOffDay } from '@Mocks/@myparcel/js-sdk/dist/data/delivery-options/getDropOffDay';

const disallowedDays = {
[MYPARCEL]: [0, 1],
[SENDMYPARCEL]: [0, 6],
const daysWithoutDelivery = {
[MYPARCEL]: [MONDAY, SUNDAY],
[SENDMYPARCEL]: [SATURDAY, SUNDAY],
};

/**
* Returns the next available delivery date, very much like the actual responses from the API. This needs to be
* quite precise because we can't mock the current date with real api responses.
* quite precise because we can't mock the current date with real api responses.
*
* @param {Object} args
* @param {Number} daysOffset
* @param {import('dayjs').Dayjs} date
*
* @returns {Object}
*/
export function getNextDeliveryOption(args, daysOffset = 1) {
const next = () => getNextDeliveryOption(args, daysOffset + 1);
export function getNextDeliveryOption(args, daysOffset = 1, date = dayjs()) {
const next = () => getNextDeliveryOption(args, daysOffset + 1, date);

const today = dayjs().add(daysOffset, 'day');
const dayOfWeek = today.weekday();
const todayIsDisallowed = disallowedDays[args.platform].includes(dayOfWeek);

let dropOffDay = dayOfWeek - 1;

const extraDelivery = extraDeliveryConfig.find((config) => {
const isToday = config.deliveryDay === dayOfWeek;
const allowedInPlatform = config.platforms.includes(args.platform);

return isToday && allowedInPlatform;
});
const currentDeliveryDate = date.add(daysOffset, 'day');
const dropOffDay = getDropOffDay(currentDeliveryDate, args.dropoff_days);
const currentDayOfWeek = currentDeliveryDate.weekday();
const todayIsDisallowed = daysWithoutDelivery[args.platform].includes(currentDayOfWeek);
const extraDelivery = findExtraDelivery(args, currentDayOfWeek);

// Skip Saturday or Monday if its setting is not enabled.
if (extraDelivery) {
// Skip Saturday or Monday if its setting is not enabled.
if (args[`${args.platform === MYPARCEL ? 'monday' : 'saturday'}_delivery`] !== 1) {
const extraDeliveryEnabled = args[`${args.platform === MYPARCEL ? 'monday' : 'saturday'}_delivery`] === 1;
const isExtraDropOffDay = dropOffDay.weekday() === extraDelivery.dropOffDay;

if (!extraDeliveryEnabled || !isExtraDropOffDay) {
return next();
}

// With Monday delivery, for example, the dropoff day is Saturday instead of Sunday.
dropOffDay = extraDelivery.dropOffDay;
} else if (todayIsDisallowed) {
return next();
}

const dropOffDays = args.dropoff_days.split(';');

// If the drop off day for today is not enabled, skip.
if (!dropOffDays.includes(dropOffDay.toString())) {
// If today is the dropoff day, check if the cutoff time has passed.
if (date.isSame(dropOffDay) && cutoffTimeHasPassed(args.cutoff_time, date)) {
return next();
}

return {
index: daysOffset,
data: getDeliveryOptionsEntry(today, !!extraDelivery),
data: getDeliveryOptionsEntry(currentDeliveryDate, !!extraDelivery),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ export const fakeDeliveryOptionsResponse = jest.fn();
* @returns {Object[]}
*/
fakeDeliveryOptionsResponse.mockImplementation((args) => {
const deliveryDaysWindow = args.deliverydays_window || 1;
const dropOffDelay = args.dropoff_delay || 0;
args.deliverydays_window = args.deliverydays_window ?? 1;
args.dropoff_delay = args.dropoff_delay ?? 0;
args.dropoff_days = args.dropoff_days ?? [0, 1, 2, 3, 4, 5, 6];

const deliveryDaysWindow = args.deliverydays_window;
const dropOffDelay = args.dropoff_delay;
let startIndex = dropOffDelay + 1;

return Array
Expand Down
2 changes: 2 additions & 0 deletions tests/dayjs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import customParseFormat from 'dayjs/plugin/customParseFormat';
import dayjs from 'dayjs';
import weekday from 'dayjs/plugin/weekday';

dayjs.extend(customParseFormat);
dayjs.extend(weekday);

export { dayjs };
13 changes: 13 additions & 0 deletions tests/helpers/createCutOffTimeDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import dayjs from 'dayjs';

/**
* @param {String} cutoffTime - String in HH:mm format.
* @param {import('dayjs').Dayjs} date
*
* @returns {import('dayjs').Dayjs}
*/
export function createCutOffTimeDate(cutoffTime, date = dayjs()) {
const [hour, minute] = cutoffTime.split(':');

return date.set('h', parseInt(hour)).set('m', parseInt(minute));
}
Loading

0 comments on commit 0e50559

Please sign in to comment.