Added new method setRelevantDates

This commit is contained in:
Alexander Cerutti
2025-01-09 00:17:19 +01:00
parent c5160c0fc9
commit 6462a852fc
4 changed files with 184 additions and 33 deletions

View File

@@ -87,6 +87,11 @@ const modelFiles = {};
const EXAMPLE_PATH_RELATIVE = "../examples/models/examplePass.pass"; const EXAMPLE_PATH_RELATIVE = "../examples/models/examplePass.pass";
/**
* @param {string} folder
* @returns
*/
function unpackFolder(folder) { function unpackFolder(folder) {
const entryList = fs.readdirSync(path.resolve(__dirname, folder)); const entryList = fs.readdirSync(path.resolve(__dirname, folder));
@@ -859,7 +864,8 @@ describe("PKPass", () => {
}); });
}); });
describe("relevant date", () => { describe("Date relevancy", () => {
describe("(deprecated iOS 18) (root).relevantDate", () => {
it("should set pass relevant date", () => { it("should set pass relevant date", () => {
pkpass.setRelevantDate(new Date("2023-04-11T00:15+10:00")); pkpass.setRelevantDate(new Date("2023-04-11T00:15+10:00"));
@@ -880,8 +886,10 @@ describe("PKPass", () => {
}); });
it("should throw if an invalid date is received", () => { it("should throw if an invalid date is received", () => {
expect(() =>
// @ts-expect-error // @ts-expect-error
expect(() => pkpass.setRelevantDate("32/18/228317")).toThrowError(); pkpass.setRelevantDate("32/18/228317"),
).toThrowError();
// @ts-expect-error // @ts-expect-error
expect(() => pkpass.setRelevantDate(undefined)).toThrowError(); expect(() => pkpass.setRelevantDate(undefined)).toThrowError();
// @ts-expect-error // @ts-expect-error
@@ -891,6 +899,75 @@ describe("PKPass", () => {
}); });
}); });
describe("setRelevantDates", () => {
it("should accept strings", () => {
pkpass.setRelevantDates([
{
startDate: "2025-01-08T22:17:30.000Z",
endDate: "2025-01-08T23:58:25.000Z",
},
{
relevantDate: "2025-01-08T22:17:30.000Z",
},
]);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.relevantDates).toMatchObject([
{
startDate: "2025-01-08T22:17:30.000Z",
endDate: "2025-01-08T23:58:25.000Z",
},
{
relevantDate: "2025-01-08T22:17:30.000Z",
},
]);
});
it("should accept dates", () => {
pkpass.setRelevantDates([
{
startDate: new Date(2025, 1, 8, 23, 58, 25),
endDate: new Date(2025, 1, 8, 23, 58, 25),
},
{
relevantDate: new Date(2025, 1, 8, 23, 58, 25),
},
]);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.relevantDates).toMatchObject([
{
startDate: "2025-02-08T22:58:25.000Z",
endDate: "2025-02-08T22:58:25.000Z",
},
{
relevantDate: "2025-02-08T22:58:25.000Z",
},
]);
});
it("should allow resetting", () => {
pkpass.setRelevantDates([
{
startDate: "2025-01-08T22:17:30.000Z",
endDate: "2025-01-08T23:58:25.000Z",
},
{
relevantDate: "2025-01-08T22:17:30.000Z",
},
]);
pkpass.setRelevantDates(null);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.relevantDates).toBeUndefined();
});
});
});
describe("barcodes", () => { describe("barcodes", () => {
it("should create all barcode structures if a message is used", () => { it("should create all barcode structures if a message is used", () => {
pkpass.setBarcodes("a test barcode"); pkpass.setBarcodes("a test barcode");

View File

@@ -956,6 +956,54 @@ export default class PKPass extends Bundle {
); );
} }
/**
* Allows setting a series of relevancy intervals or
* relevancy entries for the pass.
*
* @param {Schemas.RelevantDate[] | null} relevancyEntries
* @returns {void}
*/
public setRelevantDates(
relevancyEntries: Schemas.RelevantDate[] | null,
): void {
Utils.assertUnfrozen(this);
if (relevancyEntries === null) {
this[propsSymbol]["relevantDates"] = undefined;
return;
}
const processedDateEntries = relevancyEntries.reduce<
Schemas.RelevantDate[]
>((acc, entry) => {
try {
Schemas.validate(Schemas.RelevantDate, entry);
if (isRelevantEntry(entry)) {
acc.push({
relevantDate: Utils.processDate(
new Date(entry.relevantDate),
),
});
return acc;
}
acc.push({
startDate: Utils.processDate(new Date(entry.startDate)),
endDate: Utils.processDate(new Date(entry.endDate)),
});
} catch (err) {
console.warn(new TypeError(Messages.RELEVANT_DATE.INVALID));
}
return acc;
}, []);
this[propsSymbol]["relevantDates"] = processedDateEntries;
}
/** /**
* Allows setting a relevant date in which the OS * Allows setting a relevant date in which the OS
* should show this pass. * should show this pass.
@@ -964,6 +1012,13 @@ export default class PKPass extends Bundle {
* *
* @param {Date | null} date * @param {Date | null} date
* @throws if pass is frozen due to previous export * @throws if pass is frozen due to previous export
*
* @warning `relevantDate` property has been deprecated in iOS 18
* in order to get replaced by `relevantDates` array of intervals
* (`relevantDates[].startDate` + `relevantDates[].endDate`)
* or single date (`relevantDates[].relevantDate`). This method will
* set both the original, as the new one will get ignored in older
* iOS versions.
*/ */
public setRelevantDate(date: Date | null): void { public setRelevantDate(date: Date | null): void {
@@ -1086,3 +1141,9 @@ function validateJSONBuffer(
return Schemas.validate(schema, contentAsJSON); return Schemas.validate(schema, contentAsJSON);
} }
function isRelevantEntry(
entry: Schemas.RelevantDate,
): entry is Schemas.RelevancyEntry {
return Object.prototype.hasOwnProperty.call(entry, "relevantDate");
}

View File

@@ -43,6 +43,10 @@ export const FIELDS = {
"Cannot add field with key '%s': another field already owns this key. Ignored.", "Cannot add field with key '%s': another field already owns this key. Ignored.",
} as const; } as const;
export const RELEVANT_DATE = {
INVALID: "Cannot set relevant date. Date format is invalid",
} as const;
export const DATE = { export const DATE = {
INVALID: "Cannot set %s. Invalid date %s", INVALID: "Cannot set %s. Invalid date %s",
} as const; } as const;

View File

@@ -32,13 +32,13 @@ export const PreferredStyleSchemes = Joi.array().items(
/** /**
* A single interval can span at most 24 hours * A single interval can span at most 24 hours
*/ */
interface RelevancyInterval { export interface RelevancyInterval {
startDate: string; startDate: string | Date;
endDate: string; endDate: string | Date;
} }
interface RelevancyEntry { export interface RelevancyEntry {
relevantDate: string; relevantDate: string | Date;
} }
/** /**
@@ -52,13 +52,22 @@ interface RelevancyEntry {
export type RelevantDate = RelevancyInterval | RelevancyEntry; export type RelevantDate = RelevancyInterval | RelevancyEntry;
const RelevantDate = Joi.alternatives( export const RelevantDate = Joi.alternatives(
Joi.object<RelevancyInterval>().keys({ Joi.object<RelevancyInterval>().keys({
startDate: Joi.string().required(), startDate: Joi.alternatives(
endDate: Joi.string().required(), Joi.string().isoDate(),
Joi.date().iso(),
).required(),
endDate: Joi.alternatives(
Joi.string().isoDate(),
Joi.date().iso(),
).required(),
}), }),
Joi.object<RelevancyEntry>().keys({ Joi.object<RelevancyEntry>().keys({
relevantDate: Joi.string().required(), relevantDate: Joi.alternatives(
Joi.string().isoDate(),
Joi.date().iso(),
).required(),
}), }),
); );
@@ -492,7 +501,7 @@ export const Template = Joi.object<Template>({
*/ */
export function assertValidity<T>( export function assertValidity<T>(
schema: Joi.ObjectSchema<T> | Joi.StringSchema | Joi.Schema<T>, schema: Joi.Schema<T>,
data: T, data: T,
customErrorMessage?: string, customErrorMessage?: string,
): void { ): void {
@@ -524,7 +533,7 @@ export function assertValidity<T>(
*/ */
export function validate<T extends Object>( export function validate<T extends Object>(
schema: Joi.ObjectSchema<T> | Joi.StringSchema, schema: Joi.Schema<T>,
options: T, options: T,
): T { ): T {
const validationResult = schema.validate(options, { const validationResult = schema.validate(options, {