cumulocity-cypress
Version:
Cypress commands for Cumulocity IoT
281 lines (280 loc) • 10.5 kB
JavaScript
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;
});
}