diff --git a/packages/server/src/sdk/app/rows/tests/utils.spec.ts b/packages/server/src/sdk/app/rows/tests/utils.spec.ts index e7a9fa10eb..bed2a7fbfb 100644 --- a/packages/server/src/sdk/app/rows/tests/utils.spec.ts +++ b/packages/server/src/sdk/app/rows/tests/utils.spec.ts @@ -9,6 +9,10 @@ import { validate } from "../utils" import { generator } from "@budibase/backend-core/tests" describe("validate", () => { + const hour = () => generator.hour().toString().padStart(2, "0") + const minute = () => generator.minute().toString().padStart(2, "0") + const second = minute + describe("time only", () => { const getTable = (): Table => ({ type: "table", @@ -35,7 +39,7 @@ describe("validate", () => { it("should accept valid times with HH:mm format", async () => { const row = { - time: `${generator.hour()}:${generator.minute()}`, + time: `${hour()}:${minute()}`, } const table = getTable() const output = await validate({ table, tableId: table._id!, row }) @@ -44,13 +48,86 @@ describe("validate", () => { it("should accept valid times with HH:mm:ss format", async () => { const row = { - time: `${generator.hour()}:${generator.minute()}:${generator.second()}`, + time: `${hour()}:${minute()}:${second()}`, } const table = getTable() const output = await validate({ table, tableId: table._id!, row }) expect(output.valid).toBe(true) }) + it.each([ + ["ISO datetimes", generator.date().toISOString()], + ["random values", generator.word()], + ])("should reject %s", async (_, time) => { + const row = { + time, + } + const table = getTable() + table.schema.time.constraints = { + presence: true, + } + const output = await validate({ table, tableId: table._id!, row }) + expect(output.valid).toBe(false) + expect(output.errors).toEqual({ time: ['"time" is not a valid time'] }) + }) + + describe("time constraints", () => { + it.each(["10:00", "15:00", `10:${minute()}`, "12:34"])( + "should accept values in range (%s)", + async time => { + const row = { time } + const table = getTable() + table.schema.time.constraints = { + presence: true, + datetime: { + earliest: "10:00", + latest: "15:00", + }, + } + const output = await validate({ table, tableId: table._id!, row }) + expect(output.valid).toBe(true) + } + ) + + it.each([ + "9:59:50", + `${generator.integer({ min: 0, max: 9 })}:${minute()}`, + ])("should reject values before range (%s)", async time => { + const row = { time } + const table = getTable() + table.schema.time.constraints = { + presence: true, + datetime: { + earliest: "10:00", + latest: "15:00", + }, + } + const output = await validate({ table, tableId: table._id!, row }) + expect(output.valid).toBe(false) + expect(output.errors).toEqual({ + time: ["must be no earlier than 10:00"], + }) + }) + + it.each([ + "15:00:01", + `${generator.integer({ min: 16, max: 23 })}:${minute()}`, + ])("should reject values after range (%s)", async time => { + const row = { time } + const table = getTable() + table.schema.time.constraints = { + presence: true, + datetime: { + earliest: "10:00", + latest: "15:00", + }, + } + const output = await validate({ table, tableId: table._id!, row }) + expect(output.valid).toBe(false) + expect(output.errors).toEqual({ time: ["must be no later than 15:00"] }) + }) + }) + describe("required", () => { it("should reject empty values", async () => { const row = {} diff --git a/packages/server/src/sdk/app/rows/utils.ts b/packages/server/src/sdk/app/rows/utils.ts index 78f499f895..708bd6f503 100644 --- a/packages/server/src/sdk/app/rows/utils.ts +++ b/packages/server/src/sdk/app/rows/utils.ts @@ -224,13 +224,16 @@ function validateTimeOnlyField( ) { let res if (value && !value.match(/^(\d+)(:[0-5]\d){1,2}$/)) { - res = [`${fieldName} is not a valid time`] - } - if (constraints) { + res = [`"${fieldName}" is not a valid time`] + } else if (constraints) { let castedValue = value const stringTimeToDateISOString = (value: string) => { - const [hour, minute] = value.split(":").map((x: string) => +x) - return dayjs().hour(hour).minute(minute).toISOString() + const [hour, minute, second] = value.split(":").map((x: string) => +x) + let date = dayjs("2000-01-01T00:00:00.000Z").hour(hour).minute(minute) + if (!isNaN(second)) { + date = date.second(second) + } + return date.toISOString() } if (castedValue) {