cumulocity-cypress
Version:
Cypress commands for Cumulocity IoT
1,414 lines (1,400 loc) • 214 kB
JavaScript
'use strict';
var client = require('@c8y/client');
var _$j = require('lodash');
var semver = require('semver');
var util = require('../util-CJ8J1x_Y.js');
var localeDe = require('@angular/common/locales/de');
var localeEn = require('@angular/common/locales/en-GB');
var buildLocalizeFn = require('date-fns/locale/_lib/buildLocalizeFn');
var buildMatchFn = require('date-fns/locale/_lib/buildMatchFn');
var datefns = require('date-fns');
var setCookieParser = require('set-cookie-parser');
var libCookie = require('cookie');
var ajv = require('../ajv-qt7Q2OLe.js');
require('cypress-file-upload');
var c8yscrn_runnerHelper = require('./runner-helper.js');
require('ajv');
require('ajv-formats');
require('ajv/lib/refs/json-schema-draft-06.json');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var ___namespace = /*#__PURE__*/_interopNamespaceDefault(_$j);
var semver__namespace = /*#__PURE__*/_interopNamespaceDefault(semver);
var datefns__namespace = /*#__PURE__*/_interopNamespaceDefault(datefns);
var setCookieParser__namespace = /*#__PURE__*/_interopNamespaceDefault(setCookieParser);
var libCookie__namespace = /*#__PURE__*/_interopNamespaceDefault(libCookie);
/// <reference types="cypress" />
const C8yPactAuthObjectKeys = [
"userAlias",
"user",
"type",
];
/**
* Checks if the given object is a C8yAuthOptions.
*
* @param obj The object to check.
* @param options Options to check for additional properties.
* @returns True if the object is a C8yAuthOptions, false otherwise.
*/
function isAuthOptions(obj) {
return _$j.isObjectLike(obj) && "user" in obj && "password" in obj;
}
function toPactAuthObject(obj) {
return _$j.pick(obj, C8yPactAuthObjectKeys);
}
function isPactAuthObject(obj) {
return (_$j.isObjectLike(obj) &&
"user" in obj &&
("userAlias" in obj || "type" in obj) &&
Object.keys(obj).every((key) => C8yPactAuthObjectKeys.includes(key)));
}
function getAuthOptionsFromBasicAuthHeader(authHeader) {
if (!authHeader ||
!_$j.isString(authHeader) ||
!authHeader.startsWith("Basic ")) {
return undefined;
}
const base64Credentials = authHeader.slice("Basic ".length);
const credentials = decodeBase64(base64Credentials);
const components = credentials.split(":");
if (!components || components.length < 2) {
return undefined;
}
return { user: components[0], password: components.slice(1).join(":") };
}
function decodeBase64(base64) {
if (!base64)
return "";
let decoded;
if (typeof Buffer !== "undefined") {
decoded = Buffer.from(base64, "base64").toString("utf-8");
}
else {
decoded = atob(base64);
}
return decoded;
}
/// <reference types="cypress" />
// workaround for lodash import in Cypress nodejs typescript runtime and browser
const _$i = _$j || ___namespace;
const C8yPactModeValues = [
"record",
"recording",
"apply",
"forward",
"disabled",
"mock",
];
const C8yPactRecordingModeValues = [
"refresh",
"append",
"new",
"replace",
];
/**
* Creates an C8yPactID for a given string or array of strings.
* @param value The string or array of strings to convert to a pact id.
* @returns The pact id.
*/
function pactId(value) {
let result = "";
const suiteSeparator = "__";
const normalize = (value) => value
.split(suiteSeparator)
.map((v) => _$i.words(_$i.deburr(v), /[a-zA-Z0-9_-]+/g).join("_"))
.join(suiteSeparator);
if (value != null && _$i.isArray(value)) {
result = value.map((v) => normalize(v)).join(suiteSeparator);
}
else if (value != null && _$i.isString(value)) {
result = normalize(value);
}
if (result == null || _$i.isEmpty(result)) {
return !value ? value : undefined;
}
return result;
}
/**
* Validate the given pact mode. Throws an error if the mode is not supported
* or undefined.
* @param mode The pact mode to validate.
*/
function validatePactMode(mode) {
if (mode != null) {
const values = Object.values(C8yPactModeValues);
if (!_$i.isString(mode) ||
_$i.isEmpty(mode) ||
!values.includes(mode.toLowerCase())) {
const error = new Error(`Unsupported pact mode: "${mode}". Supported values are: ${values.join(", ")} or undefined.`);
error.name = "C8yPactError";
throw error;
}
}
}
/**
* Validate the given pact recording mode. Throws an error if the mode is not supported
* or undefined.
* @param mode The pact recording mode to validate.
*/
function validatePactRecordingMode(mode) {
if (mode != null) {
const keys = Object.values(C8yPactRecordingModeValues);
if (!_$i.isString(mode) ||
_$i.isEmpty(mode) ||
!keys.includes(mode.toLowerCase())) {
const error = new Error(`Unsupported recording mode: "${mode}". Supported values are: ${keys.join(", ")} or undefined.`);
error.name = "C8yPactError";
throw error;
}
}
}
/**
* Checks if the given object is a C8yPact. This also includes checking
* all records to be valid C8yPactRecord instances.
*
* @param obj The object to check.
* @returns True if the object is a C8yPact, false otherwise.
*/
function isPact(obj) {
return (_$i.isObjectLike(obj) &&
"info" in obj &&
_$i.isObjectLike(_$i.get(obj, "info")) &&
"records" in obj &&
_$i.isArray(_$i.get(obj, "records")) &&
_$i.every(_$i.get(obj, "records"), isPactRecord) &&
_$i.isFunction(_$i.get(obj, "nextRecord")) &&
_$i.isFunction(_$i.get(obj, "nextRecordMatchingRequest")) &&
_$i.isFunction(_$i.get(obj, "appendRecord")) &&
_$i.isFunction(_$i.get(obj, "replaceRecord")));
}
/**
* Checks if the given object is a C8yPactRecord.
*
* @param obj The object to check.
* @returns True if the object is a C8yPactRecord, false otherwise.
*/
function isPactRecord(obj) {
return (_$i.isObjectLike(obj) &&
"request" in obj &&
_$i.isObjectLike(_$i.get(obj, "request")) &&
"response" in obj &&
_$i.isObjectLike(_$i.get(obj, "response")) &&
_$i.isFunction(_$i.get(obj, "toCypressResponse")));
}
/**
* Checks if the given object is a Cypress.Response.
*
* @param obj The object to check.
* @returns True if the object is a Cypress.Response, false otherwise.
*/
function isCypressResponse(obj) {
return (_$i.isObjectLike(obj) &&
"body" in obj &&
"status" in obj &&
"headers" in obj &&
"requestHeaders" in obj &&
"duration" in obj &&
"url" in obj &&
"isOkStatusCode" in obj &&
// not a window.Response or Client.FetchResponse
!("ok" in obj || "arrayBuffer" in obj));
}
/**
* Checks if the given object is a C8yPactError. A C8yPactError is an error
* with the name "C8yPactError".
*
* @param error The object to check.
* @returns True if the object is a C8yPactError, false otherwise.
*/
function isPactError(error) {
return _$i.isError(error) && _$i.get(error, "name") === "C8yPactError";
}
function isDefined(value) {
return !_$i.isUndefined(value);
}
/**
* Converts a Cypress.Response to a C8yPactRequest.
*/
function toPactRequest(response) {
if (!response)
return response;
const result = _$i.pickBy(_$i.mapKeys(_$i.pick(response, ["url", "method", "requestHeaders", "requestBody"]), (v, k) => {
if (_$i.isEqual(k, "requestHeaders"))
return "headers";
if (_$i.isEqual(k, "requestBody"))
return "body";
return k;
}), isDefined);
if (_$i.isEmpty(result))
return undefined;
return result;
}
/**
* Converts a Cypress.Response to a C8yPactResponse.
*/
function toPactResponse(response) {
if (!response)
return response;
const result = _$i.pickBy(_$i.pick(response, [
"status",
"statusText",
"body",
"headers",
"duration",
"isOkStatusCode",
"allRequestResponses",
"$body",
]), isDefined);
if (_$i.isEmpty(result))
return undefined;
return result;
}
/**
* Returns the value of the environment variable with the given name. The function
* tries to find the value in the global `process.env` or `Cypress.env()`. If `env`
* is provided, the function uses the given object as environment.
*
* The function tries to find the value in the following order:
* - `name`
* - `camelCase(name)`
* - `CYPRESS_name`
* - `name.replace(/^C8Y_/i, "")`
* - `CYPRESS_camelCase(name)`
* - `CYPRESS_camelCase(name.replace(/^C8Y_/i, ""))`
*
* @param name The name of the environment variable.
* @param env The environment object to use. Default is `process.env` or `Cypress.env()`
*
* @returns The value of the environment variable or `undefined` if not found.
*/
function getEnvVar(name, env) {
if (!name)
return undefined;
const e = env ||
(typeof window !== "undefined" && window.Cypress
? Cypress.env()
: process.env);
function getFromEnv(key) {
return e[key];
}
function getForName(name) {
return getFromEnv(name) || getFromEnv(`CYPRESS_${name}`);
}
const plainName = name.replace(/^C8Y_/i, "");
const camelCasedName = _$i.camelCase(name).replace(/^c8Y/i, "c8y");
const camelCasedPlainName = _$i.camelCase(plainName);
return (getForName(name) ||
getForName(camelCasedName) ||
getForName(plainName) ||
getForName(camelCasedPlainName));
}
const _$h = _$j || ___namespace;
/**
* Checks if the given version satisfies the requirements provided as an array of semver ranges.
* If no required ranges are provided or range is empty, `true` is returned.
* @param version - The version to check as a string or SemVer object.
* @param requires - The required versions as semver ranges or `null` to allow version without specifying a range.
* @returns `true` if the version satisfies the requirements, `false` otherwise.
*/
function isVersionSatisfyingRequirements(version, requires) {
if (!requires || !_$h.isArrayLike(requires) || _$h.isEmpty(requires))
return true;
if (requires.length === 1 && _$h.first(requires) == null)
return true;
let result = true;
if (version != null) {
const requiredRanges = getRangesSatisfyingVersion(version, requires);
result = !_$h.isEmpty(requiredRanges);
}
else {
// null is a special placeholder to mark the test to be executed if NO system version
// is configured. Used for example for mocked tests with cy.intercept.
result = requires?.includes(null);
}
return result;
}
/**
* Returns the required semver ranges that are satisfied by the given version.
* @param version - The version to check as a string or SemVer object.
* @param requires - The required versions as semver ranges or `null` to allow version without specifying a range.
* @returns The ranges that are satisfied by the version.
*/
function getRangesSatisfyingVersion(version, requires) {
if (version == null || requires == null || _$h.isEmpty(requires)) {
return [];
}
return filterNonNull(requires)
.filter((v) => semver__namespace.satisfies(version, v))
.filter((v) => v != null);
}
/**
* Returns the minimum satisfying version for the given version and required ranges. If there is
* more than one range that is satisfied by the version, the minimum version is returned.
* @param version - The version to check as a string or SemVer object.
* @param ranges - The required versions as semver ranges or `null` to allow version without specifying a range.
* @returns The minimum satisfying version.
*/
function getMinSatisfyingVersion(version, ranges) {
const minVersions = getMinSatisfyingVersions(version, ranges);
return _$h.first(minVersions);
}
/**
* Returns all minimum satisfying versions for the given version and required ranges.
* @param version - The version to check as a string or SemVer object.
* @param ranges - The required versions as semver ranges or `null` to allow version without specifying a range.
* @returns All minimum satisfying versions for the given ranges sorted in ascending order.
*/
function getMinSatisfyingVersions(version, ranges) {
if (!version || !ranges || !_$h.isString(version) || !_$h.isArray(ranges)) {
return [];
}
if (filterNonNull(ranges).length === 0) {
return [];
}
if (_$h.isEmpty(ranges)) {
const v = semver__namespace.coerce(version);
return v ? [v] : [];
}
const minVersions = ranges.reduce((acc, range) => {
if (range != null && _$h.isString(range)) {
const coercedVersion = semver__namespace.coerce(version) ?? version;
if (semver__namespace.satisfies(coercedVersion, range)) {
const v = semver__namespace.minVersion(range);
if (v)
acc.push(v);
}
}
else {
const v = semver__namespace.coerce(version);
if (v)
acc.push(v);
}
return acc;
}, []);
return semver__namespace.sort(minVersions);
}
/**
* Returns the minimized version string for the given version. Trailing `.0` patch versions or
* `.0.0` minor versions and patch versions are omitted. If the version is a prerelease or build version,
* the full version is returned.
* @param version - The version to minimize as a string or SemVer object.
* @returns The minimized version string.
*/
function getMinimizedVersionString(version) {
const semVerObj = _$h.isString(version) ? semver__namespace.parse(version) : version;
if (semVerObj == null)
return undefined;
const props = ["major", "minor", "patch", "prerelease", "build"];
if (!props.every((prop) => prop in semVerObj)) {
return undefined;
}
if (semVerObj.patch === 0 &&
semVerObj.minor === 0 &&
!semVerObj.prerelease.length &&
!semVerObj.build.length) {
return `${semVerObj.major}`;
}
else if (semVerObj.patch === 0 &&
!semVerObj.prerelease.length &&
!semVerObj.build.length) {
return `${semVerObj.major}.${semVerObj.minor}`;
}
else {
return semVerObj.version;
}
}
/**
* Converts the given version to a semver compatible version string. This is for
* example converting `1.2` to `1.2.0`.
* @param version - The version to convert.
* @returns The semver version string.
*/
function toSemverVersion(version) {
if (version == null)
return undefined;
// version could possibly be a number, make sure to always convert to string
const result = semver__namespace.coerce(version.toString());
return result?.toString();
}
function filterNonNull(items) {
return items.filter((item) => item !== null);
}
/// <reference types="cypress" />
const { _: _$g } = Cypress;
/**
* Helper to normalize any given arguments and Cypress `prevSubject`.
*
* When chaining commands, Cypress will pass result from previous subject
* (parent) as first argument. In case of optional `prevSubject` Cypress will pass
* `null` as first argument if previous command did not yield a result value.
*
* Depending on the hierarchy of chained commands, the structure of the
* `prevSubject` argument will be different.
*
* Supported options
* 1. [<prevSubject>, arguments]
* 2. {"<index>": [<prevSubject>, arguments], "<index>": arguments }
*
* Structure of 2) depends on the command chainer.
*/
function normalizedArguments(args) {
if (!args)
return [];
let result = [];
if (_$g.isArray(args)) {
result = args;
if (args[0] != null && _$g.isArray(result[0])) {
const subjects = _$g.flatten(result[0]);
result = subjects.concat(result.slice(1));
}
}
else if (_$g.isObjectLike(args)) {
const values = Object.values(args);
result = _$g.flatten(values[0]).concat(values.slice(1));
}
return _$g.dropWhile(result, (a) => !a);
}
/**
* Get `normalizedArguments` and insert auth options from
* env variables at the beginning of the arguments.
*/
function normalizedArgumentsWithAuth(args) {
if (!args)
return [undefined];
const normalized = normalizedArguments(args);
if (_$g.isEmpty(normalized) ||
(!_$g.isEmpty(normalized) && !isAuthOptions(normalized[0]))) {
const auth = getAuthOptionsFromEnv();
if (auth) {
normalized.unshift(auth);
}
else {
if (!args[0]) {
normalized.unshift(undefined);
}
}
}
return normalized;
}
function normalizedC8yclientArguments(args) {
if (!args)
return [undefined];
const normalized = normalizedArgumentsWithAuth(args);
if (getCookieAuthFromEnv() != null && args[0] == null) {
normalized[0] = undefined;
}
return normalized;
}
function getCookieAuthFromEnv() {
const cookieAuth = new client.CookieAuth();
const token = util.get_i(cookieAuth.getFetchOptions({}), "headers.X-XSRF-TOKEN");
if (!token || _$g.isEmpty(token)) {
return undefined;
}
return cookieAuth;
}
function getAuthOptionsFromEnv() {
// check window.localStorage for __auth item
const win = cy.state("window");
const authString = win.localStorage.getItem("__auth");
if (authString && _$g.isString(authString) && !_$g.isEmpty(authString)) {
const authObj = getAuthOptionsFromArgs(JSON.parse(authString));
if (isAuthOptions(authObj)) {
return authObj;
}
}
// check auth options from test case annotation
// configured via it("...", {auth: {...}}, ...)
const auth = getAuthOptionsFromArgs(Cypress.config().auth);
if (isAuthOptions(auth)) {
return auth;
}
// check first environment variables
const user = Cypress.env(`C8Y_USERNAME`);
const password = Cypress.env(`C8Y_PASSWORD`);
if (!_$g.isEmpty(user) && !_$g.isEmpty(password)) {
return authWithTenant({
user,
password,
});
}
return undefined;
}
function getAuthOptions(...args) {
if (!args || !args.length || (args[0] == null && args.length === 1)) {
return getAuthOptionsFromEnv();
}
// first args are null for every { prevSubject: option } command in the
// call hierarchy. remove all null args from the beginning.
if (args[0] == null) {
args = _$g.dropWhile(args, (a) => !a);
}
else if (_$g.isArray(args[0])) {
args = _$g.flatten(args[0]);
}
const auth = getAuthOptionsFromArgs(...args);
if (isAuthOptions(auth)) {
return authWithTenant(auth);
}
else if (args.length === 1 && _$g.isString(args[0])) {
return undefined;
}
return getAuthOptionsFromEnv();
}
function userAliasFromArgs(...args) {
if (!args || !args.length)
return undefined;
if (args[0] == null) {
args = _$g.dropWhile(args, (a) => !a);
}
else if (_$g.isArray(args[0])) {
args = _$g.flatten(args[0]);
}
return args.length === 1 && _$g.isString(args[0]) ? args[0] : undefined;
}
function getAuthOptionsFromArgs(...args) {
// do not call getAuthOptionsFromEnv() in here!
// getAuthOptions("admin")
// return envs admin_username | admin, admin_password
if (!_$g.isEmpty(args) && _$g.isString(args[0])) {
const user = Cypress.env(`${args[0]}_username`) || args[0];
const password = Cypress.env(`${args[0]}_password`);
if (user && password) {
return authWithTenant({
user,
password,
userAlias: args[0],
});
}
}
// getAuthOptions({user: "abc", password: "abc"}, ...)
if (!_$g.isEmpty(args) && _$g.isObjectLike(args[0])) {
if (isAuthOptions(args[0])) {
return authWithTenant(_$g.pick(args[0], ["user", "password", "tenant", "userAlias", "type"]));
}
// getAuthOptions({userAlias: "abc"}, ...)
if (args[0].userAlias) {
const user = Cypress.env(`${args[0].userAlias}_username`) || args[0].userAlias;
const password = Cypress.env(`${args[0].userAlias}_password`);
if (user && password) {
return authWithTenant({
user,
password,
userAlias: args[0].userAlias,
...(args[0].type && { type: args[0].type }),
});
}
}
// getAuthOptions({user: "abc", password: "abc"}, ...)
if (args[0].username && args[0].password) {
const auth = _$g.pick(args[0], [
"username",
"password",
"tenantId",
"userAlias",
]);
delete Object.assign(auth, { user: auth.username })["username"];
if (auth.tenantId) {
delete Object.assign(auth, { tenant: auth.tenantId })["tenantId"];
}
return authWithTenant(auth);
}
// from IUser: getAuthOptions({userName: "abc", password: "abc"}, ...)
if (args[0].userName && args[0].password) {
const auth = _$g.pick(args[0], [
"userName",
"password",
"tenantId",
"userAlias",
]);
delete Object.assign(auth, { user: auth.userName })["userName"];
return authWithTenant(auth);
}
}
// getAuthOptions("abc", "abc")
if (args.length >= 2 && _$g.isString(args[0]) && _$g.isString(args[1])) {
return authWithTenant({
user: args[0],
password: args[1],
});
}
return undefined;
}
/**
* Gets and implementation of IAuthentication from the given auth options.
*/
function getC8yClientAuthentication(auth) {
let authOptions;
let result;
if (auth) {
if (_$g.isString(auth)) {
authOptions = getAuthOptions(auth);
}
else if (_$g.isObjectLike(auth)) {
if ("logout" in auth) {
result = auth;
}
else {
authOptions = auth;
}
}
}
if (!result) {
const cookieAuth = new client.CookieAuth();
const token = util.get_i(cookieAuth.getFetchOptions({}), "headers.X-XSRF-TOKEN");
if (token?.trim() && !_$g.isEmpty(token.trim())) {
result = cookieAuth;
}
else if (authOptions) {
result = new client.BasicAuth(authOptions);
}
}
return result;
}
function tenantFromBasicAuth(auth) {
if (!auth || !_$g.isObjectLike(auth) || !auth.user)
return undefined;
const components = auth.user.split("/");
if (!components || components.length < 2)
return undefined;
return components[0];
}
function authWithTenant(options) {
const tenant = Cypress.env(`C8Y_TENANT`);
if (tenant && !options.tenant) {
_$g.extend(options, { tenant });
}
return options;
}
function getSystemVersionFromEnv() {
let result = toSemverVersion(Cypress.env(`C8Y_SYSTEM_VERSION`) || Cypress.env(`C8Y_VERSION`));
if (result == null &&
Cypress.c8ypact?.isEnabled() === true &&
Cypress.c8ypact.mode() === "mock") {
const pactVersion = Cypress.c8ypact.current?.info.version?.system;
if (pactVersion) {
result = toSemverVersion(pactVersion);
}
}
return result;
}
function getShellVersionFromEnv() {
return Cypress.env(`C8Y_SHELL_VERSION`);
}
/**
* Tries to get the base URL from environment variables. The following
* environment variables are checked in order:
* - C8Y_BASEURL
* - C8Y_BASE_URL
*
* @returns Base URL from environment variables.
*/
function getBaseUrlFromEnv() {
return (getEnvVar("C8Y_BASEURL") ||
getEnvVar("C8Y_BASE_URL") ||
Cypress.config().baseUrl ||
undefined);
}
function storeClient(client) {
cy.state("c8yclient", client);
}
function restoreClient() {
return cy.state("c8yclient");
}
function resetClient() {
cy.state("c8yclient", undefined);
}
function throwError(message) {
const newErr = new Error(message);
// newErr.name = "CypressError";
throw newErr;
}
if (!Cypress.c8ypact) {
Cypress.c8ypact = {
mode: () => "disabled",
recordingMode: () => "refresh",
current: null,
getCurrentTestId: () => "-",
isRecordingEnabled: () => false,
isMockingEnabled: () => false,
savePact: () => new Promise((resolve) => resolve()),
isEnabled: () => false,
matcher: undefined,
pactRunner: undefined,
schemaGenerator: undefined,
schemaMatcher: undefined,
debugLog: false,
preprocessor: undefined,
config: {},
getConfigValue: (key, defaultValue) => defaultValue,
getConfigValues: () => ({}),
loadCurrent: () => cy.wrap(null, { log: false }),
env: () => ({}),
on: {},
createFetchClient: (auth, baseUrl) => new client.FetchClient(getC8yClientAuthentication(auth), baseUrl),
};
}
if (!Cypress.c8yctrl) {
Cypress.c8yctrl = {
mode: () => "disabled",
recordingMode: () => "append",
get current() {
return null;
},
isEnabled: () => false,
isRecordingEnabled: () => false,
isMockingEnabled: () => false,
setCurrent: () => cy.wrap(null),
resetCurrent: () => cy.wrap(null),
url: () => null,
debugLog: false,
};
}
const { _: _$f } = Cypress;
if (Cypress.semver == null) {
Cypress.semver = semver__namespace;
}
beforeEach(function () {
if (Cypress.env("C8Y_IGNORE_REQUIRES_SKIP") == null &&
// backward compatibility
Cypress.env("C8Y_PACT_IGNORE_VERSION_SKIP") == null &&
isSystemVersionSatisfyingCurrentTestRequirements() === false) {
this.skip();
}
});
/**
* Checks if `Cypress.config().requires` matches environment for the current test.
* @returns `true` if the system version satisfies the requirements of the current test, `false` otherwise.
*/
function isSystemVersionSatisfyingCurrentTestRequirements() {
const requires = Cypress.config().requires;
if (requires == null)
return true;
if (_$f.isArrayLike(requires)) {
return isVersionSatisfyingRequirements(getSystemVersionFromEnv(), requires);
}
else {
let systemResult = true;
if (requires.system != null) {
systemResult = isVersionSatisfyingRequirements(getSystemVersionFromEnv(), requires.system);
}
let shellResult = true;
if (requires.shell != null) {
shellResult = isVersionSatisfyingRequirements(getShellVersionFromEnv(), requires.shell);
}
return systemResult && shellResult;
}
}
const getAuthEnvVariables = () => {
const env = Cypress.env();
const filteredKeysAndValues = {};
Object.keys(env).forEach((key) => {
if (key.endsWith("_username") ||
key.endsWith("_password") ||
key === "C8Y_USERNAME" ||
key === "C8Y_PASSWORD") {
filteredKeysAndValues[key] = env[key];
}
});
return filteredKeysAndValues;
};
Cypress.Commands.add("getAuth", { prevSubject: "optional" }, (...args) => {
const auth = authFn("getAuth", args);
return cy.wrap(auth, { log: false });
});
Cypress.Commands.add("useAuth", { prevSubject: "optional" }, (...args) => {
const auth = authFn("useAuth", args);
if (auth != null) {
const win = cy.state("window");
win.localStorage.setItem("__auth", JSON.stringify(auth));
}
resetClient();
return cy.wrap(auth, { log: false });
});
function authFn(fnName, args) {
const auth = getAuthOptions(...args);
const userAlias = userAliasFromArgs(...args);
const consoleProps = {
getauthoptions: auth || null,
arguments: args || null,
env: getAuthEnvVariables() || null,
userAlias: userAlias || null,
};
const logger = Cypress.log({
name: fnName,
message: `${auth?.userAlias ? auth.userAlias + " -> " : ""}${auth ? auth.user : ""}`,
consoleProps: () => consoleProps,
autoEnd: false,
});
if (auth == null && userAlias != null) {
logger.end();
throw new Error(`No authentication found for userAlias ${userAlias}. Configure authentication ` +
`using ${userAlias}_username and ${userAlias}_password environment variables.`);
}
consoleProps.Yields = auth || null;
logger.end();
return auth;
}
const { _: _$e } = Cypress;
Cypress.Commands.add("hideCookieBanner", () => {
Cypress.log({
name: "hideCookieBanner",
});
cy.acceptCookieBanner(true, true, true);
});
Cypress.Commands.add("acceptCookieBanner", (required = true, functional = true, marketing = true) => {
const COOKIE_NAME = "acceptCookieNotice";
const consoleProps = {
required,
functional,
marketing,
};
Cypress.log({
name: "acceptCookieBanner",
message: "",
consoleProps: () => consoleProps,
});
const setLocalCookie = (c) => {
const cookie = JSON.stringify(c);
window.localStorage.removeItem("__ccHideCookieBanner");
Cypress.on("window:before:load", (window) => {
window.localStorage.setItem(COOKIE_NAME, cookie);
});
window.localStorage.setItem(COOKIE_NAME, cookie);
};
setLocalCookie({ required, functional, marketing });
cy.intercept({
pathname: /\/apps\/public\/public-options(@app-[^/]+)?\/options.json/,
}, (request) => {
request.on("before:response", (response) => {
if (response.statusCode !== 200) {
return;
}
if (window.localStorage.getItem("__ccHideCookieBanner") === "false") {
return;
}
const policyVersion = response.body.cookieBanner?.policyVersion;
const denyCookies = {
required: !!required,
functional: !!functional,
marketing: !!marketing,
};
if (policyVersion != null) {
denyCookies.policyVersion = policyVersion;
}
setLocalCookie(denyCookies);
});
});
});
Cypress.Commands.add("showCookieBanner", () => {
Cypress.log({
name: "showCookieBanner",
});
Cypress.on("window:before:load", (window) => {
window.localStorage.removeItem("acceptCookieNotice");
});
window.localStorage.removeItem("acceptCookieNotice");
window.localStorage.setItem("__ccHideCookieBanner", "false");
});
Cypress.Commands.add("disableCookieBanner", () => {
Cypress.log({
name: "disableCookieBanner",
message: "",
});
cy.intercept({
pathname: /\/apps\/public\/public-options(@app-[^/]+)?\/options.json/,
}, (req) => {
req.continue((res) => {
res.body.cookieBanner = undefined;
res.send();
});
});
});
Cypress.Commands.add("visitAndWaitForSelector", (url, language = "en", selector = "c8y-navigator-outlet c8y-app-icon", timeout = Cypress.config().pageLoadTimeout || 60000) => {
const consoleProps = {
url,
language,
selector,
timeout,
};
Cypress.log({
name: "visitAndWaitForSelector",
message: url,
consoleProps: () => consoleProps,
});
cy.setLanguage(language);
cy.visit(url);
cy.get(selector, { timeout }).should("be.visible");
});
Cypress.Commands.add("setLanguage", (lang) => {
globalThis.setLocale(lang);
Cypress.log({
name: "setLanguage",
message: lang,
});
cy.intercept({
method: "GET",
url: "/inventory/managedObjects?fragmentType=language*",
}, (req) => {
req.continue((res) => {
const languageFragment = req.query.fragmentType.toString();
if (res.body[languageFragment]) {
res.body[languageFragment] = lang;
}
else if (res.body.managedObjects &&
_$e.isArrayLike(res.body.managedObjects)) {
res.body.managedObjects.forEach((mo) => {
if (mo[languageFragment]) {
mo[languageFragment] = lang;
}
});
}
res.send();
});
});
window.localStorage.setItem("c8y_language", lang);
Cypress.on("window:before:load", (window) => {
window.localStorage.setItem("c8y_language", lang);
});
});
Cypress.Commands.add("disableGainsight", () => {
Cypress.log({
name: "disableGainsight",
});
cy.intercept("/tenant/system/options/gainsight/api.key*", (req) => {
req.reply({ statusCode: 404, body: {} });
throw new Error("Intercepted Gainsight API key call, but Gainsight should have been disabled. Failing...");
}).as("GainsightAPIKey");
cy.intercept("/tenant/currentTenant*", (req) => {
req.continue((res) => {
const customProperties = res.body.customProperties || {};
customProperties.gainsightEnabled = false;
res.body.customProperties = customProperties;
res.send();
});
});
});
class TrieNode {
constructor() {
this.children = new Map();
this.isEndOfWord = false;
this.count = 0;
}
}
function insertWord(root, word) {
let currentNode = root;
for (let i = 0; i < word.length; i++) {
const char = word[i];
if (!currentNode?.children.has(char)) {
currentNode?.children.set(char, new TrieNode());
}
currentNode = currentNode?.children.get(char);
if (currentNode) {
currentNode.count++;
}
}
if (currentNode) {
currentNode.isEndOfWord = true;
}
}
function shortestUniquePrefixes(words) {
const root = new TrieNode();
const prefixes = [];
for (const word of words) {
insertWord(root, word);
}
for (const word of words) {
let currentNode = root;
let prefix = "";
for (let i = 0; i < word.length; i++) {
const char = word[i];
prefix += char;
currentNode = currentNode?.children.get(char);
if (currentNode && currentNode.count === 1) {
prefixes.push(prefix);
break;
}
}
}
return prefixes;
}
// default locales to be registered automatically
const { _: _$d } = Cypress;
// https://angular.io/api/common/DatePipe#pre-defined-format-options
// https://github.com/angular/angular/blob/9847085448feff29ac6d51493e224250990c3ff0/packages/common/src/pipes/date_pipe.ts#L58
// not imported from @angular/common to avoid requiring jit at runtime
var FormatWidth;
(function (FormatWidth) {
FormatWidth[FormatWidth["Short"] = 0] = "Short";
FormatWidth[FormatWidth["Medium"] = 1] = "Medium";
FormatWidth[FormatWidth["Long"] = 2] = "Long";
FormatWidth[FormatWidth["Full"] = 3] = "Full";
})(FormatWidth || (FormatWidth = {}));
// Some i18n functions from Angular are used directly in here. This is required to not have Angular in a particular
// version as a dependency of this package. locales must be imported in the tests with the version used in the project.
// See as sources:
// https://github.com/angular/angular/tree/6f5dabe0d25a5660b7c3001041449b4622dd8924/packages/core/src/i18n
// https://github.com/angular/angular/tree/6f5dabe0d25a5660b7c3001041449b4622dd8924/packages/common/src/i18n
// https://github.com/angular/angular/blob/6f5dabe0d25a5660b7c3001041449b4622dd8924/packages/common/src/i18n/locale_data_api.ts
// https://github.com/angular/angular/blob/6f5dabe0d25a5660b7c3001041449b4622dd8924/packages/core/src/i18n/locale_data_api.ts
var NgLocaleDataIndex;
(function (NgLocaleDataIndex) {
NgLocaleDataIndex[NgLocaleDataIndex["LocaleId"] = 0] = "LocaleId";
NgLocaleDataIndex[NgLocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
NgLocaleDataIndex[NgLocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
NgLocaleDataIndex[NgLocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
NgLocaleDataIndex[NgLocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
NgLocaleDataIndex[NgLocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
NgLocaleDataIndex[NgLocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
NgLocaleDataIndex[NgLocaleDataIndex["Eras"] = 7] = "Eras";
NgLocaleDataIndex[NgLocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
NgLocaleDataIndex[NgLocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
NgLocaleDataIndex[NgLocaleDataIndex["DateFormat"] = 10] = "DateFormat";
NgLocaleDataIndex[NgLocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
NgLocaleDataIndex[NgLocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
NgLocaleDataIndex[NgLocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
NgLocaleDataIndex[NgLocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
NgLocaleDataIndex[NgLocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
NgLocaleDataIndex[NgLocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
NgLocaleDataIndex[NgLocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
NgLocaleDataIndex[NgLocaleDataIndex["Currencies"] = 18] = "Currencies";
NgLocaleDataIndex[NgLocaleDataIndex["Directionality"] = 19] = "Directionality";
NgLocaleDataIndex[NgLocaleDataIndex["PluralCase"] = 20] = "PluralCase";
NgLocaleDataIndex[NgLocaleDataIndex["ExtraData"] = 21] = "ExtraData";
NgLocaleDataIndex[NgLocaleDataIndex["DfnsLocale"] = 22] = "DfnsLocale";
})(NgLocaleDataIndex || (NgLocaleDataIndex = {}));
const LOCALE_DATA = {};
function getNgLocaleId(locale) {
const data = getNgLocale(locale);
return data[NgLocaleDataIndex.LocaleId];
}
async function registerLocale(data, c8yLocaleId, extraData = undefined, localeId) {
const angularId = normalizeLocaleId(c8yLocaleId);
LOCALE_DATA[angularId] = data;
if (extraData) {
LOCALE_DATA[angularId][NgLocaleDataIndex.ExtraData] = extraData;
}
const dfnsLocale = await loadDfnsLocale(getNgLocaleId(c8yLocaleId), localeId);
LOCALE_DATA[angularId][NgLocaleDataIndex.DfnsLocale] = {
...dfnsLocale,
localize: {
...dfnsLocale?.localize,
month: buildLocalizeFn({
values: monthValuesForLocale(angularId),
defaultWidth: "wide",
}),
day: buildLocalizeFn({
values: dayValuesForLocale(angularId),
defaultWidth: "wide",
}),
},
// node_modules/date-fns/locale/en-US/_lib/match/index.js
match: {
...dfnsLocale?.match,
month: buildMatchFn({
matchPatterns: matchMonthPatterns(angularId),
defaultMatchWidth: "wide",
parsePatterns: parseMonthPatterns(angularId),
defaultParseWidth: "any",
}),
day: buildMatchFn({
matchPatterns: matchDayPatterns(angularId),
defaultMatchWidth: "wide",
parsePatterns: parseDayPatterns(angularId),
defaultParseWidth: "any",
}),
},
};
}
async function registerDefaultLocales() {
await registerLocale(
// @ts-expect-error
!isModule(localeDe) ? localeDe : localeDe.default, "de");
await registerLocale(
// @ts-expect-error
!isModule(localeEn) ? localeEn : localeEn.default, "en");
}
function normalizeLocaleId(localeId) {
return localeId.toLowerCase().replace(/_/g, "-");
}
function getNgLocale(localeId) {
const getNgLocaleData = (localeId) => {
const normalizedLocale = normalizeLocaleId(localeId);
if (!(normalizedLocale in LOCALE_DATA)) {
LOCALE_DATA[normalizedLocale] =
// @ts-expect-error
globalThis.ng?.common?.locales?.[normalizedLocale];
}
return LOCALE_DATA[normalizedLocale];
};
const normalizedLocale = normalizeLocaleId(localeId);
let match = getNgLocaleData(normalizedLocale);
if (match) {
return match;
}
// let's try to find a parent locale
const parentLocale = normalizedLocale.split("-")[0];
match = getNgLocaleData(parentLocale);
if (match) {
return match;
}
throw new Error(`Missing locale data for the locale "${localeId}".`);
}
function localizedTimeFormat(localeId = "en", formatWidth = FormatWidth.Short) {
return getLocaleTimeFormat(localeId, formatWidth);
}
function localizedDateFormat(localeId = "en", formatWidth = FormatWidth.Short) {
return getLocaleDateFormat(localeId, formatWidth);
}
function localizedDateTimeFormat(localeId = "en", formatWidth = FormatWidth.Short) {
const fullTime = getLocaleTimeFormat(localeId, formatWidth);
const fullDate = getLocaleDateFormat(localeId, formatWidth);
return formatDateTime(getLocaleDateTimeFormat(localeId, formatWidth), [
fullTime,
fullDate,
]);
}
// https://github.com/angular/angular/blob/fe691935091aaf7090864c8111a15f7cc7e53b6c/packages/common/src/i18n/format_date.ts#L201
function formatDateTime(str, opt_values) {
{
str = str.replace(/\{([^}]+)}/g, function (match, key) {
return opt_values != null && key in opt_values ? opt_values[key] : match;
});
}
return str;
}
function parseDate(date, format) {
let parsedDate = undefined;
// try to parse as number fist, if string is passed it might be converted without format being used
if (_$d.isNumber(date)) {
parsedDate = new Date(date);
}
// parse with format
if (!isValidDate(parsedDate) && _$d.isString(date)) {
parsedDate = Cypress.datefns.parse(date, format, new Date());
// if (!isValidDate(parsedDate) && _.isString(date)) {
// parsedDate = new Date(date);
// }
}
return parsedDate;
}
function isValidDate(date) {
return date != null && !isNaN(date) && _$d.isDate(date);
}
function isModule(module) {
return (
// @ts-expect-error
module && _$d.isObject(module) && module.default && !_$d.isEmpty(module.default));
}
function getLastDefinedValue(data, index) {
for (let i = index; i > -1; i--) {
if (typeof data[i] !== "undefined") {
return data[i];
}
}
throw new Error("Locale data API: locale data undefined");
}
function getLocaleTimeFormat(locale, width) {
const data = getNgLocale(locale);
return getLastDefinedValue(data[NgLocaleDataIndex.TimeFormat], width);
}
function getLocaleDateFormat(locale, width) {
const data = getNgLocale(locale);
return getLastDefinedValue(data[NgLocaleDataIndex.DateFormat], width);
}
function getLocaleDateTimeFormat(locale, width) {
const data = getNgLocale(locale);
const dateTimeFormatData = data[NgLocaleDataIndex.DateTimeFormat];
return getLastDefinedValue(dateTimeFormatData, width);
}
async function loadDfnsLocale(angularLocaleId, dfnsLocaleId) {
const load = async (locale) => {
try {
const l = await import(`date-fns/locale/${locale}/`);
return l.default;
}
catch (e) {
console.error(e);
return null;
}
};
if (!angularLocaleId && !dfnsLocaleId)
return null;
const r = load(dfnsLocaleId ?? angularLocaleId);
if (r) {
return r;
}
return null;
}
// var parseDayPatterns = {
// narrow: [/^s/i, /^m/i, /^t/i, /^w/i, /^t/i, /^f/i, /^s/i],
// any: [/^su/i, /^m/i, /^tu/i, /^w/i, /^th/i, /^f/i, /^sa/i]
// };
function parseDayPatterns(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const dayData = l[NgLocaleDataIndex.DaysStandalone] ?? l[NgLocaleDataIndex.DaysFormat];
const result = {
narrow: dayData[0].map((m) => new RegExp("^" + _$d.lowerCase(m).substring(0, 1), "i")),
any: shortestUniquePrefixes(dayData[2]).map((m) => new RegExp("^" + _$d.lowerCase(m), "i")),
};
return result;
}
// var matchDayPatterns = {
// narrow: /^[smdmf]/i,
// short: /^(so|mo|di|mi|do|fr|sa)/i,
// abbreviated: /^(son?|mon?|die?|mit?|don?|fre?|sam?)\.?/i,
// wide: /^(sonntag|montag|dienstag|mittwoch|donnerstag|freitag|samstag)/i
// };
function matchDayPatterns(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const dayData = l[NgLocaleDataIndex.DaysStandalone] ?? l[NgLocaleDataIndex.DaysFormat];
const result = {
narrow: new RegExp("^[" + _$d.uniq(dayData[0]).join("|") + "]", "i"),
short: new RegExp("^(" + _$d.uniq(dayData[3]).join("|") + ")", "i"),
abbreviated: new RegExp("^(" + dayData[1].join("|") + ")", "i"),
wide: new RegExp("^(" + dayData[2].join("|") + ")", "i"),
};
return result;
}
function parseMonthPatterns(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const monthData = l[NgLocaleDataIndex.MonthsStandalone] ?? l[NgLocaleDataIndex.MonthsFormat];
const result = {
narrow: monthData[0].map((m) => new RegExp("^" + _$d.lowerCase(m).substring(0, 1), "i")),
any: shortestUniquePrefixes(monthData[2]).map((m) => new RegExp("^" + _$d.lowerCase(m), "i")),
};
return result;
}
function matchMonthPatterns(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const monthData = l[NgLocaleDataIndex.MonthsStandalone] ?? l[NgLocaleDataIndex.MonthsFormat];
const result = {
narrow: new RegExp("^[" + _$d.uniq(monthData[0]).join("|") + "]", "i"),
abbreviated: new RegExp("^(" + monthData[1].join("|") + ")", "i"),
wide: new RegExp("^(" + monthData[2].join("|") + ")", "i"),
};
return result;
}
function monthValuesForLocale(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const monthData = l[NgLocaleDataIndex.MonthsStandalone] ?? l[NgLocaleDataIndex.MonthsFormat];
const result = {
narrow: monthData[0],
abbreviated: monthData[1],
wide: monthData[2],
};
return result;
}
function dayValuesForLocale(locale) {
const l = getNgLocale(locale);
if (!l)
return null;
const monthData = l[NgLocaleDataIndex.DaysStandalone] ?? l[NgLocaleDataIndex.DaysFormat];
const result = {
narrow: monthData[0],
abbreviated: monthData[1],
wide: monthData[2],
};
return result;
}
const { _: _$c } = Cypress;
Cypress.datefns = datefns__namespace;
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 && _$c.isArray(l)) {
Cypress.datefns.setDefaultOptions({
locale: l[NgLocaleDataIndex.DfnsLocale],
});
}
};
const isISODateSource = (arg) => {
return arg != null && (_$c.isString(arg) || _$c.isNumber(arg) || _$c.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 = _$c.last(args);
}
}
return [source, _$c.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 loc