UNPKG

cumulocity-cypress

Version:
281 lines (280 loc) 10.5 kB
import { FormatWidth, NgLocaleDataIndex, getNgLocale, getNgLocaleId, isValidDate, localizedDateFormat, localizedDateTimeFormat, localizedTimeFormat, parseDate, registerDefaultLocales, registerLocale, } from "../locale/locale"; import * as datefns from "date-fns"; import { throwError } from "../utils"; const { _ } = Cypress; Cypress.datefns = datefns; const defaultOptions = { log: true, invalid: "ignore", strictFormats: true, }; globalThis.registerLocale = registerLocale; globalThis.registerDefaultLocales = registerDefaultLocales; (async () => { await registerDefaultLocales(); })(); globalThis.setLocale = (localeId) => { const l = getNgLocale(localeId); if (l && _.isArray(l)) { Cypress.datefns.setDefaultOptions({ locale: l[NgLocaleDataIndex.DfnsLocale], }); } }; const isISODateSource = (arg) => { return arg != null && (_.isString(arg) || _.isNumber(arg) || _.isArray(arg)); }; const fromArguments = (args) => { let source = undefined; let options = undefined; if (args.length === 1) { source = args[0]; } else if (args.length > 1) { if (isISODateSource(args[1]) || typeof args[1] === "string") { source = args[1]; if (args.length > 2) { options = args[2]; } } else { source = args[0]; options = _.last(args); } } return [source, _.defaults({}, options, defaultOptions)]; }; Cypress.Commands.add("toDate", { prevSubject: "optional" }, (prevSubject, ...args) => { const [unsafeSource, options] = fromArguments([prevSubject, ...args]); const localizedFormats = options != null ? prepareLocalizedFormats(options) : []; const win = cy.state("window"); const language = options?.language ?? win.localStorage.getItem("c8y_language") ?? "en"; const consoleProps = options?.consoleProps ?? {}; if (!consoleProps.options) { consoleProps.options = options || null; } consoleProps.language = `${language} (${getNgLocaleId(language)})`; consoleProps.localizedFormats = localizedFormats || null; consoleProps.source = unsafeSource || null; let logger = options?.logger; let ourlogger = false; if (options?.log === true && options?.consoleProps == null) { logger = Cypress.log({ name: "toDate", message: `${unsafeSource || null}`, consoleProps: () => consoleProps, autoEnd: false, }); ourlogger = true; } if (!unsafeSource) { logger?.end(); throwError(`No or undefined source provided to cy.toDate.`); } const source = unsafeSource; const input = Array.isArray(source) ? source : [source]; const formats = []; let dates = input.map((item) => { let parsedDate; // try to read date from Angular date formats or number for (const format of localizedFormats) { parsedDate = parseDate(item, format); if (isValidDate(parsedDate)) { formats.push(format); return parsedDate; } } // try to read as ISO date if (!isValidDate(parsedDate) && _.isString(item)) { parsedDate = Cypress.datefns.parseISO(item); if (isValidDate(parsedDate)) { formats.push(undefined); } } // try to read as Date last. this might have some unexpected result if (!isValidDate(parsedDate) && options?.strictFormats === false) { parsedDate = new Date(item); if (isValidDate(parsedDate)) { formats.push(undefined); } } if (isValidDate(parsedDate)) { return parsedDate; } if (options?.invalid === "throw") { throwError(`'${item?.toString()}' could not be converted into a valid date. No matching format or invalid input.`); } return undefined; }); if (options?.invalid === "ignore") { dates = dates.filter((date) => date); if (_.isEmpty(dates)) { if (ourlogger) { logger?.end(); } return undefined; } } const result = Array.isArray(source) ? dates : dates[0]; consoleProps["Format"] = (Array.isArray(source) ? formats : formats[0]) || null; consoleProps["Yielded"] = result || null; if (ourlogger) { logger?.end(); } cy.wrap(result, { log: false }); }); Cypress.Commands.add("toISODate", { prevSubject: "optional" }, (prevSubject, ...args) => { const [source, options] = fromArguments([prevSubject, ...args]); const consoleProps = { source: source || null, options, }; let log = undefined; if (options?.log === true || options?.log == null) { log = Cypress.log({ name: "toISODate", message: `${source || null}`, consoleProps: () => consoleProps, autoEnd: false, }); } const o = { ...options, ...{ log: false, consoleProps, logger: log } }; cy.toDate(source, o).then((dates) => { const sources = Array.isArray(source) ? source : [source]; const d = Array.isArray(dates) ? dates : [dates]; let isoStrings = d.map((date, index) => { const defaultValue = options?.invalid === "keep" ? sources[index] : undefined; return date ? date.toISOString() : defaultValue; }); if (options?.invalid === "ignore") { isoStrings = isoStrings.filter((iso) => iso); if (_.isEmpty(isoStrings)) { log?.end(); return cy.wrap(Array.isArray(source) ? [] : undefined, { log: false, }); } } const result = Array.isArray(source) ? isoStrings : isoStrings[0]; consoleProps["Dates"] = dates || null; consoleProps["Yielded"] = result || null; log?.end(); cy.wrap(result, { log: false }); }); }); Cypress.Commands.add("dateFormat", { prevSubject: "optional" }, (prevSubject, ...args) => { const [source, opts] = fromArguments([prevSubject, ...args]); const options = _.pick(opts, ["invalid", "language", "log"]); const localizedFormats = prepareLocalizedFormats(options); const win = cy.state("window"); const language = options?.language ?? win.localStorage.getItem("c8y_language") ?? "en"; const consoleProps = { source, options, language: `${language} (${getNgLocaleId(language)})`, localizedFormats, }; if (options?.log === true) { Cypress.log({ name: "dateFormat", message: source, consoleProps: () => consoleProps, }); } if (!source) { throwError(`No or undefined provided source provided to cy.dateFormat.`); } const format = findDateFormatForSource(source, localizedFormats); if (!format && options?.invalid === "throw") { throwError(`'${source?.toString()}' could not be converted into a valid date. No matching format or invalid input.`); } consoleProps.yielded = format; cy.wrap(format, { log: false }); }); Cypress.Commands.add("compareDates", // @ts-expect-error { prevSubject: "optional" }, (prevSubject, source, target, options = defaultOptions) => { if ((!source && prevSubject) || (_.isObjectLike(source) && !_.isArray(source))) { source = prevSubject; } if (_.isString(target)) { target = Cypress.datefns.parseISO(target); if (!target) { throwError(`${target} is not a valid ISO formatted date.`); } } const localizedFormats = prepareLocalizedFormats(options).reverse(); const win = cy.state("window"); const language = options?.language ?? win.localStorage.getItem("c8y_language") ?? "en"; const consoleProps = { source, target, options, language: `${language} (${getNgLocaleId(language)})`, localizedFormats, }; if (options?.log === true) { Cypress.log({ name: "compareDates", message: source, consoleProps: () => consoleProps, }); } consoleProps.target = target; const unsafeFortmat = findDateFormatForSource(source, localizedFormats); consoleProps.format = unsafeFortmat; if (!unsafeFortmat) { if (options?.invalid === "throw") { throwError(`'${source?.toString()}' could not be converted into a valid date. No matching format or invalid input.`); } else { return cy.wrap(false); } } const format = unsafeFortmat; const formattedTarget = Cypress.datefns.format(target, format); consoleProps.formattedTarget = formattedTarget; if (formattedTarget) { return cy.wrap(_.isEqual(source, formattedTarget)); } else { throwError(`'${target?.toString()}' could not be formatted as string using ${format}.`); } return cy.wrap(false); }); function findDateFormatForSource(source, localizedFormats) { if (!source) return undefined; for (const format of localizedFormats) { if (isValidDate(parseDate(source, format))) { return format; } } return undefined; } function prepareLocalizedFormats(options) { const win = cy.state("window"); const language = options?.language ?? win.localStorage.getItem("c8y_language") ?? "en"; const formatWidths = options?.formatWidth ? [options.formatWidth] : Object.values(FormatWidth).filter((n) => _.isNumber(n)); let localizedFormats; if (options?.format) { localizedFormats = [options?.format]; } else { const dateTimeFormats = formatWidths.map((f) => localizedDateTimeFormat(language, f)); const dateFormats = formatWidths.map((f) => localizedDateFormat(language, f)); const timeFormats = formatWidths.map((f) => localizedTimeFormat(language, f)); localizedFormats = [...dateTimeFormats, ...dateFormats, ...timeFormats]; } // date-fns does not use z...zzzz. fix or converion will fail // https://github.com/date-fns/date-fns/issues/2088 return localizedFormats.map((format) => { let result = format.replace("zzzz", "'GMT'X"); result = result.replace("z", "X"); return result; }); }