Skip to content
Snippets Groups Projects
Commit 24c28b9f authored by Sebastian Pongratz's avatar Sebastian Pongratz :ping_pong:
Browse files

Merge branch '#1084955_cypress' into '2021.2.0'

#1084955: cypress into basic

See merge request xrm/basic!1301
parents b3213aee 65226cdf
No related branches found
No related tags found
No related merge requests found
Showing
with 15832 additions and 0 deletions
describe("Press different buttons", () =>
{
it([Tag.DEFAULT], "Should click a button in a dropdown", () =>
{
cy.login();
cy.openContext("Person", "PersonFilter_view", PresentationMode.FILTER);
cy.selectValue(0);
cy.pressButton("Serienaktionen", ButtonType.DROPDOWN);
cy.pressButton("Zu Kampagne hinzufügen", ButtonType.DROPDOWNITEM);
cy.get(".neon-preview-display[data-test-display-entity-name='CampaignAddParticipants_entity']").should('be.visible');
});
it([Tag.DEFAULT], "Should click the edit button", () =>
{
cy.login();
cy.openContext("Person", "PersonFilter_view", PresentationMode.FILTER);
cy.selectValue(0);
cy.pressButton(1);
cy.url().should('include', "edit");
});
it([Tag.DEFAULT], "Should click a button in the preview dropdown menu", () =>
{
cy.login();
cy.openContext("Person", "PersonFilter_view", PresentationMode.FILTER);
cy.selectValue(0);
cy.pressButton(0, ButtonType.THREE_POINT_PREVIEW);
cy.pressButton(4, ButtonType.DROPDOWNITEM);
cy.get(".neon-preview-display[data-test-display-entity-name='CampaignAddParticipants_entity']").should('be.visible');
});
});
\ No newline at end of file
describe("Filter Person data", () => {
it([Tag.DEFAULT], "Should open main view and then edit", () =>
{
cy.login();
cy.openMainFromFilter("Person", 4);
cy.openEditFromMain();
});
});
describe("Drawer actions person", () =>
{
it([Tag.DEFAULT], "Should close, open, edit, cancel drawers in the Preview", () =>
{
cy.login();
cy.openMainFromFilter("Person", 2);
cy.drawerPreview("Kommunikation", DrawerAction.CLOSE, "Communications");
cy.drawerPreview("Kommunikation", DrawerAction.OPEN, "Communications");
cy.drawerPreview("Kommunikation", DrawerAction.EDIT, "Communications");
cy.drawerPreview("Kommunikation", DrawerAction.CANCEL, "Communications");
});
});
describe("Set a favorite person", () => {
it([Tag.DEFAULT], "Should set a favorite person without tag", () =>
{
cy.login();
cy.setFavorite(8, null, "Person"); //careful! The number needs to be adjusted if you have i.e. more favorites
});
it([Tag.DEFAULT], "Should set a favorite person with tag", () =>
{
cy.login();
cy.setFavorite(1, "myPersonTag", "Person"); //careful! The number needs to be adjusted if you have i.e. more favorites with that tag
});
});
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const tagify = require('cypress-tags');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('file:preprocessor', tagify(config));
};
\ No newline at end of file
/// <reference types="cypress" />
declare namespace Cypress
{
interface Chainable
{
/**
* presses the [+] button in a view
*/
pressInsert(
): Chainable<any>;
/**
* presses a button in a view (not the [+] button)
*
*@param pValue : number of button (without writing!), start's with 0 | string: writing in the button
*@param pButtonType : use if the button has a specific type (preview, dropdown, dropdown-preview)
* dropdown: presses a button that opens a dropdown
* dropdownItem: presses a button in the dropdown
* preview: presses a button in the preview
* three-points: presses the [...] button to open the dropdown; the value is irrelevant for this
* three-points-preview: presses the [...] button in the preview to open the dropdown; the value is irrelevant for this
*/
pressButton(
pValue: number | string,
pButtonType? : ButtonType
): Chainable<any>;
/**
* presses save button in edit view
*/
pressSave(
): Chainable<any>;
/**
* presses cancel button in edit view
*/
pressCancel(
): Chainable<any>;
/**
* presses an on/off switch in an edit-view
*
* @param pFieldName name of the field
*/
pressSwitch(
pFieldName: string,
): Chainable<any>;
}
}
Cypress.Commands.add('pressInsert', () =>
{
cy.wait(100);
cy.get(".neon-button-strip .neon-button-strip-chunk .neon-icononly-button").first().click({force: true});
cy.wait(1000);
});
Cypress.Commands.add('pressButton', (pValue, pButtonType) =>
{
cy.wait(100);
if(pButtonType == ButtonType.THREE_POINT_PREVIEW)
{
cy.get(".neon-preview-display .neon-button-strip .neon-button-strip-chunk .neon-icononly-square-accent-button").first().click({force: true});
}
if(pButtonType == ButtonType.THREE_POINT)
{
cy.get(".neon-main-display .neon-button-strip .neon-button-strip-chunk .neon-icononly-square-accent-button").first().click({force: true});
}
if(typeof pValue == "number" && pButtonType != ButtonType.THREE_POINT && pButtonType != ButtonType.THREE_POINT_PREVIEW)
{
switch(pButtonType)
{
case ButtonType.PREVIEW:
cy.get(".neon-preview-display .neon-button-strip .neon-button-strip-chunk .neon-icononly-accent-button").eq(pValue).click({force: true});
break;
case ButtonType.DROPDOWN:
cy.get(".neon-main-display .neon-button-strip .neon-button-strip-chunk .neon-icononly-accent-button").eq(pValue).click({force: true});
break;
case ButtonType.DROPDOWNITEM:
cy.get(".popupContent .v-menubar-menuitem").eq(pValue).click({force: true});
break;
default:
cy.get(".neon-main-display .neon-button-strip .neon-button-strip-chunk .neon-icononly-accent-button").eq(pValue).click({force: true});
break;
}
}
else if(typeof pValue == "string" && pButtonType != ButtonType.THREE_POINT && pButtonType != ButtonType.THREE_POINT_PREVIEW)
{
switch(pButtonType)
{
case ButtonType.DROPDOWN:
cy.get(".neon-main-display .neon-button-strip .neon-button-strip-chunk .neon-dropdown-button").contains(pValue, {matchCase: false}).click({force: true});
break;
case ButtonType.DROPDOWNITEM:
cy.get(".popupContent .v-menubar-menuitem .v-menubar-menuitem-caption").contains(pValue, {matchCase: false}).click({force: true});
break;
default:
cy.get(".neon-main-display .neon-button-strip .neon-button-strip-chunk .neon-accent-button").contains(pValue, {matchCase: false}).click({force: true});
break;
}
}
});
Cypress.Commands.add('pressSave', () =>
{
cy.wait(100);
cy.saveEdit();
});
Cypress.Commands.add('pressCancel', () =>
{
cy.wait(100);
cy.get(".neon-display-content-edit-buttons .neon-accent-button").first().click();
});
Cypress.Commands.add('pressSwitch', (pFieldName) =>
{
cy.wait(100);
cy.get(".neon-viewtemplate-generic[data-test-component-name='Edit']"
+ " .neon-placeholder-wrapping[data-test-component-type='ON_OFF_SWITCH'][data-test-component-vt-generic-field='" + pFieldName + "']"
+ " .neon-on-off-switch").click({force:true});
});
\ No newline at end of file
/// <reference types="cypress" />
declare namespace Cypress
{
interface Chainable
{
/**
* Will log in with the given parameters.
*
* @param pUsername Username to use for the login.
* @param pPassword Password to use for the login. Can be null.
* @param pLocale Locale to use. Can be null, but defaults to "de-DE")
*/
login(
pUsername?: string,
pPassword?: string,
pLocale?: string
)
: Chainable;
}
}
/**
* Implements the command which runs the login for the given parameters. This also accepts an optional locale.
*/
Cypress.Commands.add('login', (pUsername, pPassword, pLocale) =>
{
// Clear the cookie to get a new session on page reload
cy.clearCookie('JSESSIONID');
cy.viewport("macbook-16");
if(!pUsername && !pPassword)
{
pUsername = AdminLogin.USER;
pPassword = AdminLogin.PASSWORD;
}
if(!pPassword && pUsername)
{
pPassword = "";
}
// Will define the final locale which will be applied to the Cookie.
// By default, this will fall back to adminLogin.LOCALE.
let finalLocale: string = AdminLogin.LOCALE;
// If the environment variable is set, then use it.
const configDefaultLanguage = Cypress.env('DEFAULT_LOCALE')
if (configDefaultLanguage)
{
finalLocale = configDefaultLanguage;
}
// If a locale has been explicitly set, then use this one and override the config.
if(pLocale)
{
finalLocale = pLocale;
}
// Set the Cookie for the locale.
cy.setCookie("locale", finalLocale);
// Load the login route
cy.visit("/client/login");
// Fill the login form
cy.get(".neon-login-form > .neon-login-input-user").first().type(pUsername);
// As the password is not mandatory we can just skip it of not explicitly set.
if (pPassword)
{
cy.get(".neon-login-form > .neon-login-input-password").first().type(pPassword);
}
// Click the login submit button
cy.get(".neon-login-form > .neon-login-action-submit").first().click();
// Wait until the client is ready.
cy.get(".neon-root-container");
// Additional cool-down after login. Under some circumstances the first interaction after the login might be a
// bit slow.
cy.wait(500);
})
/// <reference types="cypress" />
declare namespace Cypress
{
interface Chainable
{
/**
* Opens the given {@param view} of the given {@param context} in the given {@param presentationMode}.
* There is no additional validation on those properties. If the view does not exist, the test will most likely
* fail. The {@param presentationMode} is a union type because the values are fixed and will not change over
* a long period of time. This also supports better autocompletion.
*
* Use {@param parameters} to add additional Query-Parameters to the URL. This may be used to add the 'id'
* Query-Parameter to the URL when opening a main view.
*
* @param pContext Name of the context to open.
* @param pView Name of the view to open
* @param pPresentationMode Presentation mode to open the view in.
* @param pParameters Parameters to add to the URL.
*/
openContext(
pContext: string,
pView: string,
pPresentationMode: PresentationMode,
pParameters?: { [key: string]: string },
): Chainable<any>;
/**
* Will open the global menu of the client. If the global menu could not be found or could not be opened, then
* test will fail.
*
* @return JQuery<HTMLElement> Element which wraps the popup (call button and content).
*/
openGlobalMenu(): Chainable<JQuery>;
/**
* Will type a value in the index search of the client and opens the first result
*
* @return JQuery<HTMLElement> Element which wraps the popup (call button and content).
*/
openIndexSearch(
pValue : string
): Chainable<JQuery>;
}
}
Cypress.Commands.add('openGlobalMenu', () =>
{
return cy.get(".neon-navigation-bar-popup[data-test-misc-popup-type='CONTEXT_GROUPS']")
.should('be.visible')
.first()
.click()
.get(".neon-navigation-bar-popup[data-test-misc-popup-type='CONTEXT_GROUPS'] > .neon-navigation-button ~ .is-popup-container")
.should("be.visible")
.get(".neon-navigation-bar-popup[data-test-misc-popup-type='CONTEXT_GROUPS']")
.first();
});
Cypress.Commands.add('openContext', (pContextName, pViewName, pPresentationMode, pParameters) =>
{
// Define the basic query parameters.
const queryParameters: { [key: string]: string } = {
"view": pViewName,
...pParameters
};
// Add additional parameters here if necessary.
// Build the URL for the context to open
const url = `/client/${pContextName}/${pPresentationMode.toLowerCase()}?${new URLSearchParams(queryParameters).toString()}`;
// Go to the built URL.
cy.visit(url);
// Define all attributes which can validate that the view has been opened.
const validateAttributes = {
"data-test-display-context-name": pContextName,
"data-test-display-view-name": pViewName,
};
// Build the select which matches all the defined 'validateAttributes'.
const selector = Object.keys(validateAttributes)
.map(value => `[${value}='${validateAttributes[value]}']`)
.join("");
// Wait until the view becomes visible.
cy.get(selector, { timeout: 10000 });
});
//Opens the index search, enters a word, searches for that and opens the first result
Cypress.Commands.add('openIndexSearch', (pValue) =>
{
return cy.get(".neon-navigation-icon-button")
.should('be.visible')
.first()
.click()
.get(".neon-indexsearch-search-input")
.first()
.click()
.type(pValue + "{enter}")
.get(".neon-indexsearch-result-entry-container")
.wait(10000) //this wait() is needed to avoid an error
.get(".neon-indexsearch-result-entry-container")
.children() // if children() gets deleted the opened result will be random
.first()
.click();
});
// Defines all valid presentation modes. As the above Union is just for the type definition we need to
// add a runtime check.
enum AdminLogin
{
USER = 'admin',
PASSWORD = "",
LOCALE = "de_DE",
}
(window as { AdminLogin?: typeof AdminLogin }).AdminLogin = AdminLogin;
enum PresentationMode
{
FULL = "full",
FILTER = "filter",
EDIT = "edit"
};
(window as { PresentationMode?: typeof PresentationMode }).PresentationMode = PresentationMode;
enum ButtonType
{
DROPDOWN = "dropdown",
DROPDOWNITEM = "dropdownItem",
PREVIEW = "preview",
THREE_POINT = "three-points",
THREE_POINT_PREVIEW = "three-points-preview"
};
(window as { ButtonType?: typeof ButtonType }).ButtonType = ButtonType;
enum DrawerAction
{
OPEN = "open",
CLOSE = "close",
EDIT = "edit",
SAVE = "save",
CANCEL = "cancel"
};
(window as { DrawerAction?: typeof DrawerAction }).DrawerAction = DrawerAction;
enum FilterType
{
TEXT = "text",
COMBO = "combobox",
LOOKUP = "lookup",
DATE = "date",
NUMBER = "number"
};
(window as { FilterType?: typeof FilterType }).FilterType = FilterType;
\ No newline at end of file
/// <reference types="cypress" />
declare namespace Cypress
{
interface Chainable
{
/**
* gets the location of a simple edit field
*
* @param pFieldName name of the field
*/
getEditField(
pFieldName: string,
): Chainable<any>;
/**
* gets the location of a large edit field
*
* @param pFieldName name of the field
*/
getLargeEditField(
pFieldName: string,
): Chainable<any>;
/**
* gets the location of an advanced html field
*
* @param pFieldName name of the field
*/
getHTMLField(
pFieldName: string,
): Chainable<any>;
/**
* gets the location of a date field
*
* @param pFieldName name of the field
*/
getDateField(
pFieldName: string,
): Chainable<any>;
/**
* gets a lookupfield
*
* @param pFieldName name of the field
*/
getLookUpField(
pFieldName: string,
): Chainable<any>;
/**
* selects value in a table (first that matches the given string or the rownumber)
*
* @param pValue name of the value as shown in the view
* @param pViewType set to true if the view is a tree (i.e. Attributes)
*/
selectValue(
pValue: string | number,
pViewType?: "tree" | "tile"
): Chainable<any>;
/**
* fills a lookup field
*
* @param pFieldName name of the field
* @param pText text to type and then choose from
*/
fillLookUpField(
pFieldName: string,
pText: string,
): Chainable<any>;
/**
* fills a combobox field
*
* @param pFieldName name of the field
* @param pText text to type and then choose from
*/
fillComboField(
pFieldName: string,
pText: string,
): Chainable<any>;
/**
* click save button in current edit-view
*
*@param pDsgvo true when person to confirm dsgvo message
*/
saveEdit(
pDsgvo?: boolean
): Chainable<any>;
/**
* delete the newly created data set
*
*@param pContext give the name of the context to assert that you are in the right one
*/
deleteAfterCreation(
pContext?: string
): Chainable<any>;
/**
* upload a document (the document has to be in "fixtures" of cypress) in preview
*
*@param pDocumentName the document you want to upload i.e. adito.png
*@param pType of the document i.e. image/png
*/
uploadDocument(
pDocumentName: string,
pType: string
): Chainable<any>;
/**
* gets the location of a simple edit field in a multi edit
* for example: Address, Communication, Attribute
*
* @param pFieldName name of the field
*/
getMultiEditField(
pFieldName: string,
): Chainable<any>;
}
}
Cypress.Commands.add('getEditField', (pFieldName) =>
{
cy.get(".neon-viewtemplate-generic[data-test-component-name='Edit'] .neon-textfield[data-test-component-type='EDITFIELD'][data-test-component-vt-generic-field='" + pFieldName + "']");
});
Cypress.Commands.add('getLargeEditField', (pFieldName) =>
{
cy.get(".neon-viewtemplate-generic[data-test-component-name='Edit'] .neon-textarea[data-test-component-type='MEMOFIELD'][data-test-component-vt-generic-field='" + pFieldName + "']");
});
Cypress.Commands.add('getHTMLField', (pFieldName) =>
{
cy.get(".neon-viewtemplate-generic[data-test-component-name='Edit'] .neon-richtextarea[data-test-component-vt-generic-field='" + pFieldName + "'] iframe")
.then(($element) =>
{
const $body = $element.contents().find("body");
let stripe = cy.wrap($body);
stripe.find("p").dblclick();
});
});
Cypress.Commands.add('getDateField', (pFieldName) =>
{
cy.get(".neon-viewtemplate-generic[data-test-component-name='Edit'] .neon-datefield[data-test-component-vt-generic-field='" + pFieldName + "']");
});
Cypress.Commands.add('getLookUpField', (pFieldName) =>
{
cy.get(".neon-lookup-field[data-test-component-vt-generic-field='" + pFieldName + "']");
});
Cypress.Commands.add('selectValue', (pValue, pViewType) =>
{
var tree = "";
switch(pViewType)
{
case "tile":
if(typeof pValue == "string")
{
cy.get(".tiles .tile .details .title").contains(pValue).first().click();
}
else
{
cy.get('.tiles .tile:nth(' + pValue + ') .details')
.scrollIntoView()
.click();
}
break;
case "tree":
tree = "tree"; //no break! - should continue
default:
if(typeof pValue == "string")
{
cy.get(".v-" + tree + "grid-cell-content").contains(pValue).first().click();
}
else
{
cy.get('.v-' + tree + 'grid .v-' + tree + 'grid-body > .v-' + tree + 'grid-row:nth(' + pValue + ')')
.scrollIntoView()
.find("> .v-" + tree + "grid-cell:nth(0)")
.click();
}
break;
}
});
Cypress.Commands.add('fillLookUpField', (pFieldName, pText) =>
{
cy.get(".neon-lookup-field[data-test-component-vt-generic-field='" + pFieldName + "']").then($lookup =>
{
cy.get(".neon-lookup-field[data-test-component-vt-generic-field='" + pFieldName + "'] .v-textfield").first().type(pText);
cy.wait(1000);
cy.get(".neon-lookup-content-default[data-test-component-lookup-overlay-identification='" + $lookup.attr("data-test-component-lookup-identification") + "'] .v-grid-row > .v-grid-cell").first()
.first().wait(500).click({force: true});
});
});
Cypress.Commands.add('fillComboField', (pFieldName, pText) =>
{
cy.get(".neon-combobox[data-test-component-vt-generic-field='" + pFieldName + "'] .v-filterselect-input").clear();
cy.get(".neon-combobox[data-test-component-vt-generic-field='" + pFieldName + "'] .v-filterselect-input").first().type(pText + "{enter}");
});
Cypress.Commands.add('saveEdit', (pDsgvo) =>
{
cy.get(".neon-button-strip-menu > .neon-button-strip-menu-do-action").first().click({force: true});
cy.get(".neon-button-strip-menu > .neon-button-strip-menu-do-action").first().click({force: true});
if(pDsgvo == true)
{
cy.get(".neon-question-window .neon-window-footer .neon-accent-button").first().click({force: true});
}
});
Cypress.Commands.add('deleteAfterCreation', (pContext) =>
{
cy.wait(5000);
if(pContext)
{
cy.url().should('include', pContext);
}
cy.get(".neon-button-strip-chunk .neon-icononly-square-accent-button", {timeout: 10000}).last().click();
cy.get(".popupContent .v-menubar-menuitem").contains("Löschen").click();
cy.get(".neon-question-window .neon-window-footer .neon-button").first().click();
});
Cypress.Commands.add('getMultiEditField', (pFieldName) =>
{
cy.get(".neon-textfield[data-test-component-name='" + pFieldName + "']");
});
Cypress.Commands.add('uploadDocument', (pDocumentName, pDocType) =>
{
cy.fixture(pDocumentName).then(logoImage =>
{
var dt = new DataTransfer();
var file = new File([Cypress.Blob.base64StringToBlob(logoImage, pDocType)], pDocumentName, {type: pDocType});
dt.items.add(file);
cy.get(".neon-upload[data-test-component-vt-generic-field='BINDATA_UPLOAD']")
.first()
.trigger('drop', {dataTransfer: dt});
cy.get(".neon-upload[data-test-component-vt-generic-field='BINDATA_UPLOAD'] .neon-label").should("have.text", pDocumentName);
});
});
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable {
/**
* filters a given field in a filterview
*
* @param pFilterName name of the filter
* @param pFilterValue value that is filtered for
* @param pType of filter value that is filtered for
* @param pComparisons an array of objects with your comparisontype and the number of rows that should be returned in the filterview. i.e. [{type: 'enthält nicht', count: 1}]
* if this is null ALL comparisons for the type are used, CAREFUL: No assertions will take place and this is not possible for filter-type 'date'
* @param pContext enter the context to be filtered (if entered the filter_view will open)
*/
filterField(
pFilterName: string,
pFilterValue: string,
pType: FilterType,
pComparisons: Array<{type: string, count: number}>,
pContext?: string,
): Chainable<any>;
/**
* resets all filters of a filterview
*
* @param pContext enter the context to be filtered (if entered the filter_view will open)
*/
resetFilter(
pContext?: string,
): Chainable<any>;
/**
* groups a given field in a filterview
*
* @param pGroupName name of the group field to enter
* @param pGroupCount number of groups that should be visible
* @param pContext enter the context to be filtered (if entered the filter_view will open)
*/
groupField(
pGroupName: string,
pGroupCount: number,
pContext?: string,
): Chainable<any>;
/**
* groups multiple fields in a filterview
*
* @param pGroupNameArray names of the group fields to enter i.e. ["Sprache","Status"]
* @param pGroupCount umber of groups that should be visible after adding the fields
* @param pContext enter the context to be filtered (if entered the filter_view will open)
*/
groupMultiFields(
pGroupNameArray: Array<string>,
pGroupCount: number,
pContext?: string,
): Chainable<any>;
/**
* resets all groupings of a filterview
*
* @param pContext enter the context to be filtered (if entered the filter_view will open)
*/
resetGroup(
pContext?: string,
): Chainable<any>;
}
}
Cypress.Commands.add('filterField', (pFilterName, pFilterValue, pType, pComparisons, pContext) =>
{
if(pComparisons == null && pType == FilterType.DATE)
{
throw new Error("Using all comparisons is not possible for filter-type 'date'");
}
if(context)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
cy.resetFilter(pContext);
//enters the filtername
cy.get(".neon-table-view-filter-controls > .v-button").click();
cy.get(".neon-preview-display[data-test-display-type='filter'] .neon-drawer[data-test-misc-filter-leaf-empty='true'] .neon-filter-leaf-input-field").last().type(pFilterName);
cy.get(".neon-preview-display[data-test-display-type='filter'] .neon-drawer[data-test-misc-filter-leaf-empty='true'] .neon-filter-leaf-input-field").last().type("{enter}");
if(pComparisons != null)
{
pComparisons.forEach((comparison: { type: string; count: number; }) =>
{
let comparisonType = comparison.type;
let comparisonCount = comparison.count;
//enter comparison type
cy.get(".neon-filter-leaf-input-operator .v-filterselect-input").last().clear();
cy.get(".neon-filter-leaf-input-operator .v-filterselect-input").last().type(comparisonType, { force: true });
cy.get(".neon-filter-leaf-input-operator .v-filterselect-input").last().type("{enter}", { force: true });
if(comparisonType != 'ist leer' && comparisonType != 'ist nicht leer')
{
//enter the filtervalue
switch(pType)
{
case FilterType.TEXT:
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='text'] > input").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='text'] > input").type(pFilterValue + "{enter}", { force: true });
break;
case "number":
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='number'] > input").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='number'] > input").type(pFilterValue + "{enter}", { force: true });
break;
case FilterType.COMBO:
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='combobox'] .neon-combobox > input").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='combobox'] .neon-combobox > input").type(pFilterValue + "{enter}", { force: true });
break;
case FilterType.LOOKUP:
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='lookup'] .neon-lookup > input").last().clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='lookup'] .neon-lookup > input").last().type(pFilterValue, { force: true });
cy.get(".neon-lookup-content .neon-box-layout[data-test-layout-type='BOX'] .v-grid-body > .v-grid-row").first().click();
break;
case FilterType.DATE:
switch(comparisonType)
{
case 'innerhalb (Zeitspanne)':
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='relative-date-selector'] .neon-combobox > input").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='relative-date-selector'] .neon-combobox > input").type(pFilterValue + "{enter}", { force: true });
break;
case 'vergangene':
case 'kommende':
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='relative-date-selector'] .neon-combobox > input").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='relative-date-selector'] .neon-combobox > input").type(pFilterValue + "{enter}", { force: true });
break;
case 'gleich':
case 'ungleich':
case 'kleiner als':
case 'kleiner oder gleich':
case 'größer als':
case 'größer oder gleich':
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='neon-date'] .v-datefield-textfield").clear();
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='neon-date'] .v-datefield-textfield").type(pFilterValue + "{enter}", { force: true });
break;
}
break;
}
}
//assert if the number of rows is what you expected
cy.wait(1000);
cy.get(".row-count-indicator").contains('span', comparisonCount).should('be.visible');
});
}
else
{
_compareAll(pFilterValue, pType);
}
});
Cypress.Commands.add('resetFilter', (pContext) =>
{
if(pContext)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
cy.get(".neon-table-view-filter-condition-header .neon-link-button").click({force: true});
cy.wait(1000);
});
/**
* help function to go through all different comparisons (does not use assertion, is not possible for filtertype 'date')
*
* @param type of filter value that is filtered for
* @param filterValue value that is filtered for
*/
function _compareAll(pFilterValue : string, pType : string)
{
//array for all possible comparison types
var compTypes = ["gleich", "ungleich"];
//add more comparison types and type in the filter value
switch(pType)
{
case FilterType.TEXT:
compTypes.push("enthält", "enthält nicht", "beginnt mit", "endet mit");
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='text'] > input").type(pFilterValue + "{enter}", { force: true });
break;
case "number":
compTypes.push("kleiner als", "kleiner oder gleich", "größer als", "größer oder gleich");
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='number'] > input").type(pFilterValue + "{enter}", { force: true });
break;
case FilterType.COMBO:
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='combobox'] .neon-combobox > input").type(pFilterValue + "{enter}", { force: true });
break;
case FilterType.LOOKUP:
cy.get(".neon-filter-leaf-container-input[data-test-misc-filter-input-type='lookup'] .neon-lookup > input").last().type(pFilterValue, { force: true });
cy.get(".neon-lookup-content .neon-box-layout[data-test-layout-type='BOX'] .v-grid-body > .v-grid-row").first().click();
break;
}
compTypes.push("ist leer", "ist nicht leer");
//go through all comparison types
compTypes.forEach(function(comType)
{
cy.get(".neon-filter-leaf-input-operator .v-filterselect-input").last().clear();
cy.get(".neon-filter-leaf-input-operator .v-filterselect-input").last().type(comType + "{enter}");
cy.wait(1000);
});
}
Cypress.Commands.add('groupField', (pGroupName, pGroupCount, pContext) =>
{
if(pContext)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
cy.resetGroup();
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .neon-combobox").type(pGroupName);
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .neon-combobox").type("{enter}");
cy.get(".neon-tree-table[data-test-component-type='TREETABLE'] tbody").find("tr").should('have.length', pGroupCount);
//assert if group is set
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .taglist").contains(pGroupName).should('be.visible');
});
Cypress.Commands.add('groupMultiFields', (pGroupNameArray, pGroupCount, pContext) =>
{
if(pContext)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
cy.resetGroup();
pGroupNameArray.forEach(function(groupName: string)
{
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .neon-combobox > input").type(groupName + "{enter}", { force: true });
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .neon-combobox > input").type("{enter}", { force: true });
cy.wait(1000);
//assert if group is set
cy.get(".neon-table-view-filter-grouping-layout .neon-tagfield .taglist").contains(groupName).should('be.visible');
});
cy.get(".neon-tree-table[data-test-component-type='TREETABLE'] tbody").find("tr").should('have.length', pGroupCount);
});
Cypress.Commands.add('resetGroup', (pContext) =>
{
if(pContext)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
cy.get(".neon-table-view-filter-grouping-layout .neon-link-button").first().click({force: true});
cy.wait(1000);
});
\ No newline at end of file
import './authentication'
import './context'
import './fields'
import './filter'
import './lookup'
import './view'
import './actions'
import './indexer'
import './internal/taggingTests'
import 'cypress-wait-until'
import './enum.ts'
// Cypress fails the test if any browser-side exception occurs. As those exceptions are out of scope of the customizing, we can just ignore them here.
Cypress.on('uncaught:exception', () =>
{
// Returning false here prevents Cypress from failing the test.
return false;
});
\ No newline at end of file
/// <reference types="cypress" />
declare namespace Cypress
{
interface Chainable
{
/**
* Searches for a string in the indexsearch component of ADITO
*
* @param pSearchWord word that is typed into the index search field
*/
searchIndex(
pSearchWord: string
):Chainable<any>;
/**
* Opens a resultset of a search in the indexgroup
*
* @param pResult string of the search that should be opened
*/
openIndexResult(
pResult: string
):Chainable<any>;
}
}
Cypress.Commands.add('searchIndex', (pSearchWord) =>
{
cy.wait(1000);
cy.get(".neon-navigation-icon-button").first().click();
cy.wait(500);
cy.get(".neon-indexsearch-search-input").first().type(pSearchWord);
cy.wait(500);
cy.get(".show-more").first().click();
});
Cypress.Commands.add('openResult', (pResult) =>
{
cy.wait(500)
cy.get(".neon-navigation-icon-button").first().click();
cy.get(".v-textfield-neon-indexsearch-search-input").first().type(pResult);
cy.wait(500);
cy.get(".neon-indexsearch-result-entry").first().click();
cy.wait(500);
});
\ No newline at end of file
enum Tag {
'DEFAULT' = 0,
'QA' = 1,
}
\ No newline at end of file
/// <reference types="cypress" />
import OpenLookupWithSearchOptions = Cypress.OpenLookupWithSearchOptions;
import OpenLookupWithDropdownOptions = Cypress.OpenLookupWithDropdownOptions;
import Chainable = Cypress.Chainable;
declare namespace Cypress
{
interface Chainable<Subject = any>
{
/**
* Opens the lookup overlay based on search query entered into the lookup field. This is solely based on
* feature, that the lookup opens the overlay when the user enters a search query into the lookup field.
* If the overlay is any kind of grid, then this function may also select the nth row automatically
* (-> See {@link OpenLookupSelectableOptions.select}).
* This will also return the opened overlay element. But be careful, because it may already be closed after
* this function completes.
*
* @example {@code
* // #get(...) must resolve to the element with the class 'neon-lookup-field'.
* cy.get('.neon-lookup-field[data-test-component-vt-generic-field="ORGANISATION_CONTACTID"]')
* .openLookupWithSearch({query: 'query', select: 0})
* }
* @param pOptions Defines how the lookup will be opened and if any row shall be selected.
* @param pIsTree set to true if the lookup includes a tree (i.e. Attributes)
*
* @return {Chainable} Overlay element of the lookup.
*/
openLookupWithSearch(
pOptions: OpenLookupWithSearchOptions,
pIsTree? : boolean
): Chainable<JQuery>;
/**
* Opens the lookup through it's dropdown button without entering any value in the lookup field. This basically
* means, that there is not filter set and the lookup may show all available data.
* If the overlay is any kind of grid, then this function may also select the nth row automatically
* (-> See {@link OpenLookupSelectableOptions.select}).
* This will also return the opened overlay element. But be careful, because it may already be closed after
* this function completes.
*
* @example {@code
* // #get(...) must resolve to the element with the class 'neon-lookup-field'.
* cy.get('.neon-lookup-field[data-test-component-vt-generic-field="ORGANISATION_CONTACTID"]')
* .openLookupWithDropdown({select: 0})
* }
* @param pOptions
* @param pIsTree set to true if the lookup includes a tree (i.e. Attributes)
*/
openLookupWithDropdown(
pOptions: OpenLookupWithDropdownOptions,
pIsTree? : boolean
): Chainable<JQuery>;
}
/**
* Defines the available options for all functions which are able to open a lookup and select the nth row.
*/
interface OpenLookupSelectableOptions
{
/**
* If the nth row of the potentially opened grid shall be selected. If null/undefined nothing will be selected.
* If {@code true} then the first row will be selected and if any positive integer is given then the row on
* this index will be selected. (Index starts at 0)
*/
select?: boolean | number
}
/**
* Defines the available options for {@link #openLookupWithSearch}.
*/
interface OpenLookupWithSearchOptions extends OpenLookupSelectableOptions
{
/**
* Query which shall be entered on the lookup field. This must not be null, but may be an empty string.
*/
query: string,
}
/**
* Defines the available options for {@link #openLookupWithDropdown}
*/
interface OpenLookupWithDropdownOptions extends OpenLookupSelectableOptions{}
}
Cypress.Commands.add('openLookupWithSearch', {prevSubject: 'element'}, (pSubject, pOptions: OpenLookupWithSearchOptions, pIsTree: boolean) =>
{
// Validate that the given subject is a lookup element.
validateElementIsLookup(pSubject);
// Enter the search query on the lookup text field.
cy.get(pSubject).find('> .neon-lookup > input').type(pOptions.query);
var tree = "";
if(pIsTree)
{
tree = "tree";
}
return getLookupOverlay(pSubject).then(innerSubj =>
{
// If the 'select' option is defined, then click the nth row in the grid.
const sel = pOptions.select
if (sel != null)
{
const nthElement = (typeof sel) == 'number' ? sel as number : 0;
cy.get(innerSubj as any).find('.v-' + tree + 'grid .v-' + tree + 'grid-body > .v-' + tree + 'grid-row:nth(' + nthElement + ')')
.scrollIntoView()
.find("> .v-" + tree + "grid-cell:nth(0)")
.click();
}
// Return the overlay element as subject.
return cy.get(innerSubj as any);
});
});
Cypress.Commands.add('openLookupWithDropdown', {prevSubject: 'element'}, (pSubject, pOptions: OpenLookupWithDropdownOptions, pIsTree: boolean) =>
{
// Validate that the given subject is a lookup element.
validateElementIsLookup(pSubject);
// Click on the dropdown button.
cy.get(pSubject).find('> .neon-lookup > .neon-inline-popup-button').click();
var tree = "";
if(pIsTree)
{
tree = "tree";
}
return getLookupOverlay(pSubject).then(innerSubj =>
{
// If the 'select' option is defined, then click the nth row in the grid.
const sel = pOptions.select;
if (sel != null)
{
const nthElement = (typeof sel) == 'number' ? sel as number : 0;
cy.get(innerSubj as any).find('.v-' + tree + 'grid .v-' + tree + 'grid-body > .v-' + tree + 'grid-row:nth(' + nthElement + ')')
.scrollIntoView()
.find("> .v-" + tree + "grid-cell:nth(0)")
.click();
}
// Return the overlay element as subject.
return cy.get(innerSubj as any);
});
});
/**
* Validates that the given {@param element} is a valid lookup. If it's no lookup, then an exception will be thrown.
* @param element
*/
function validateElementIsLookup(pElement: JQuery): void
{
// If the subject is no lookup, then just throw an exception, because the state is invalid.
if (!pElement.hasClass('neon-lookup-field'))
{
throw new Error('Element is no valid lookup');
}
}
/**
* Returns the overlay element based on the given lookup element, which is defined through {@param parent}. The given
* element is expected to be a lookup, if it's not an exception will be thrown.
*
* @param parent Parent element for which the overlay shall be returned.
* @return Element which represents the matching overlay part of the lookup.
*/
function getLookupOverlay(pParent: JQuery): Chainable<JQuery>
{
return cy.get(pParent as any).wait(150).then(subject =>
{
// Validate that the given element is a lookup.
validateElementIsLookup(pParent);
// Matching overlay pair of the lookup is defined through the following attribute.
const lookupIdentification = subject.attr('data-test-component-lookup-identification');
// Build the query selector which must match the Lookup content.
const overlaySelector = '[data-test-component-lookup-overlay-identification="' + lookupIdentification + '"]';
// Wait until the overlay is available and is visible on the UI.
return cy.get(overlaySelector);
});
}
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable {
/**
* opens edit view. Can only be used when already in main view
*
* @param pContext if entered the filter_view of that context will open
* @param pItemNumber which item in the Filterview should be opened; default: 0
* @param pDataTestComponentName needed when the filter view is not a table (i.e. Activities -> VIEWTEMPLATE_TIMELINE)
*/
openMainFromFilter(
pContext?: string,
pItemNumber?: number,
pDataTestComponentName?: string
): Chainable<any>;
/**
* opens edit view. Can only be used when already in main view (HINT: Use openMainFromFilter)
*
*/
openEditFromMain(
): Chainable<any>;
/**
* Opens certain tab. Can only be used when already in main view (HINT: Use openMainFromFilter)
*
* @param pTabName name of tab as viewed in client
*/
openTabInMain(
pTabName: string,
): Chainable<any>;
/**
* Opens, closes or edits and saves/cancels drawer in preview
*
* @param pDrawerName name of drawer as viewed in client
* @param pAction what you want to do with the drawer (save and cancel are only available after edit)
* @param pDataTestComponentName needed to edit the drawer - otherwise the edit button cannot be found (i.e. "Communications")
*/
drawerPreview(
pDrawerName: string,
pAction: DrawerAction,
pDataTestComponentName?: string
): Chainable<any>;
/**
* Sets a dataset as favorite
*
* @param pExpectedTotal total number of expected favorites (in combination with a tag, the tag is filtered first)
* @param pFavoriteTag if set a favorite tag will be added to the favorite aswell
* @param pContext if set the first item of a filter_view will open in main view
* @param pDataTestComponentName needed when the filter view of the context is not a table (i.e. Activities -> VIEWTEMPLATE_TIMELINE)
*
* cy.setFavorite(1, "myTag", "Organisation");
*/
setFavorite(
pExpectedTotal: number,
pFavoriteTag?: string,
pContext?: string,
pDataTestComponentName?: string
): Chainable<any>;
/**
* Asserts that there is no popup from an error message coming
* (as cypress is able to still work in the background despite a popup showing)
*
*/
assertNoPopup(
): Chainable<any>;
}
}
Cypress.Commands.add('openMainFromFilter', (pContext, pItemNumber, pDataTestComponentName) =>
{
if(pContext)
{
cy.openContext(pContext, pContext + "Filter_view", PresentationMode.FILTER, {});
}
if(!pItemNumber)
{
pItemNumber = 0;
}
if(!pDataTestComponentName)
{
pDataTestComponentName = "VIEWTEMPLATE_TABLE";
}
switch(pDataTestComponentName)
{
case "VIEWTEMPLATE_TIMELINE":
cy.get(".neon-grid-timeline .v-grid-tablewrapper tbody > tr").eq(pItemNumber).dblclick();
break;
case "VIEWTEMPLATE_TREETABLE":
cy.get(".neon-tree-table .v-treegrid-tablewrapper tbody > tr").eq(pItemNumber).dblclick();
break;
default:
cy.get(".neon-table .v-grid-tablewrapper tbody > tr").eq(pItemNumber).dblclick();
break;
}
if(pContext)
{
cy.url().should('include', pContext);
}
cy.url().should('include', "full");
});
Cypress.Commands.add('openEditFromMain', () =>
{
cy.get(".neon-button-strip .neon-button-strip-chunk .user-action").first().click();
cy.url().should('include', "edit");
});
Cypress.Commands.add('openTabInMain', (pTabName) =>
{
cy.url().should('include', "full");
cy.get(".neon-register-header .neon-register-tab-header .v-button-wrap .v-button-caption").contains(pTabName, { matchCase: false }).click({force: true});
//no assertion because the active element is fixed and thus "is-active" is not visible for cypress at this point
});
Cypress.Commands.add('drawerPreview', (pDrawerName, pAction, pDataTestComponentName) =>
{
if(!pDataTestComponentName && pAction == 'edit')
{
throw new Error("You need the 'data test component name' to start editing a drawer component in the preview");
}
cy.wait(500);
switch(pAction)
{
case DrawerAction.EDIT:
cy.get(".neon-drawer[data-test-component-name='" + pDataTestComponentName + "'] .edit").first().click({force: true});
cy.get(".neon-display-template-edit-buttons .neon-button").should('be.visible');
break;
case DrawerAction.SAVE:
cy.get(".neon-display-template-edit-buttons .neon-button").first().click({force: true});
break;
case DrawerAction.CANCEL:
cy.get(".neon-display-template-edit-buttons .neon-icononly-accent-button").click({force: true});
break;
default:
cy.get(".neon-drawer-header").contains(pDrawerName, { matchCase: false }).click({force: true});
if(pAction == DrawerAction.CLOSE)
{
cy.get(".neon-drawer.collapsed").contains(pDrawerName, { matchCase: false }).should('be.visible');
}
else if(pAction == DrawerAction.OPEN)
{
cy.get(".neon-drawer.expanded").contains(pDrawerName, { matchCase: false }).should('be.visible');
}
break;
}
});
Cypress.Commands.add('setFavorite', (pExpectedTotal, pFavoriteTag, pContext, pDataTestComponentName) =>
{
if(pContext)
{
cy.openMainFromFilter(pContext, 0, pDataTestComponentName);
}
cy.wait(500);
cy.get(".favorite-wrapper .favorite-button").click();
if(pFavoriteTag)
{
cy.get(".favorite-wrapper .neon-tagfield").click();
cy.get(".favorite-wrapper .neon-tagfield").type(pFavoriteTag + "{enter}");
cy.filterField("Gruppenname", pFavoriteTag, FilterType.COMBO, [{type: "gleich", count: pExpectedTotal}], "Favorite");
}
else
{
cy.openContext("Favorite", "FavoriteFilter_view", PresentationMode.FILTER, {});
cy.get(".row-count-indicator").contains('span', pExpectedTotal).should('be.visible');
}
});
Cypress.Commands.add('assertNoPopup', () =>
{
cy.get(".neon-checkpoint-window .popupContent").should('not.exist');
});
\ No newline at end of file
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "cypress-wait-until", "cypress-tags"]
},
"include": [
"**/*.ts"
]
}
import * as supertest from 'supertest'
import { expect } from 'chai'
const passwordBase64 = process.env.passwordssp // TODO set environment or replace this
const username = process.env.usernamessp // TODO set environment or replace this
describe("Basic", () => {
describe("nonauthenticated", () => {
it("should login", async () => {
const response = await createPost("testlogin", JSON.stringify({ "username": username, "password": passwordBase64 }), false)
.expect(200)
// TODO add assertions to login
})
})
describe("auth", () => {
let token
before("load token", async () => {
const response = await createPost("testlogin", JSON.stringify({ "username": username, "password": passwordBase64 }), false)
.expect(200)
token = response.body;
})
it("should load systems", async () => {
const response = await createPost("listSystems", JSON.stringify({ "username": username, "jwt": token }), false)
.expect(200);
const jsonResponse = JSON.parse(response.body)
expect(Object.keys(jsonResponse).length).is.eq(6) // TODO this number have to be adjusted
expect(jsonResponse["0"]).to.be.not.null
expect(jsonResponse["0"].length).is.eq(9)
})
describe("listSystems", () => {
let systemId
before("load system", async () => {
const response = await createPost("listSystems", JSON.stringify({ "username": username, "jwt": token }), false)
.expect(200)
const jsonResponse = JSON.parse(response.body)
systemId = jsonResponse["4"][0];
})
it("should system running", async () => {
const response = await createPost("systemIsRunning", JSON.stringify({ "username": username, "jwt": token, "systemId": systemId }), false)
.expect(200)
// TODO assert that system is running
})
})
})
})
function createPost(url, data, json) {
let test = supertest("https://ssp.adito.cloud")
.post("/services/rest/" + url)
.set("Content-Type", "application/json")
.set("Connection", "keep-alive")
.send(data)
.buffer(true)
if (json)
return test
return test.parse((res, cb) => {
let data = Buffer.from("");
res.on("data", chunk => data = Buffer.concat([data, chunk]))
res.on("end", () => cb(null, data.toString()))
})
}
\ No newline at end of file
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["mocha"]
},
"include": [
"**/*.ts"
]
}
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment