From fa441d3d82b9aa238bf68a9bdcc33f7d8f6ec361 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 9 Jan 2024 15:16:47 +0000 Subject: [PATCH] feat: LEAP-355: Enhance Relation helpers and fix ts typings (#23) * feat: LEAP-355: Enhance Relation helpers and fix ts typings * Enhance ToolBar with additional helper methods Added more detailed commentary to the existing functions in ToolBar.ts. Furthermore, enhanced the functionalities by introducing new functions for interacting with annotation elements. These new features and methods will help in interacting with the annotations dropdown, creating new annotations, and selecting specific annotations from the list. * Rename Relation methods for clarity The 'relation' function was renamed to 'getRelation', and the hover/unhover functions were renamed to 'hoverOverRelation' and 'stopHoveringOverRelation'. This was done to improve readability and better express the functionalities of these methods in the LSF Relations helper file. * Add blur event after filing relation lebel input --- helpers/LSF/Relations.ts | 200 ++++++++++++++++++++++++++++++++++++++- helpers/LSF/ToolBar.ts | 58 +++++++++++- tsconfig.json | 5 +- types/Cypress.d.ts | 3 +- 4 files changed, 259 insertions(+), 7 deletions(-) diff --git a/helpers/LSF/Relations.ts b/helpers/LSF/Relations.ts index aa67322..77e8890 100644 --- a/helpers/LSF/Relations.ts +++ b/helpers/LSF/Relations.ts @@ -1,13 +1,209 @@ +import Chainable = Cypress.Chainable; + +const DIRECTION = { + LEFT: 'left', + RIGHT: 'right', + BOTH: 'bi', +}; + +type Direction = typeof DIRECTION[keyof typeof DIRECTION]; + +type RelationIdxArgs = [idx: number]; +type RelationFromToArgs = [from: string, to: string]; +type RelationArgs = RelationIdxArgs | RelationFromToArgs; + export const Relations = { + DIRECTION, + /** + * Get relation by index or by from/to labels + * @param args + */ + getRelation(...args: RelationArgs): Chainable> { + if (args.length === 1) { + const idx: number = args[0]; + + return cy.get('.lsf-relations .lsf-relations__item').eq(idx); + } else { + const [from, to]: RelationFromToArgs = args; + + return cy.get('.lsf-relations').contains(from).closest('.lsf-relations').contains(to); + } + }, + /** + * Check number of existed relations + * @param count + */ hasRelations(count: number) { cy.get('.lsf-details__section-head').should('have.text', `Relations (${count})`); }, - hasRelation(from: string, to: string) { - cy.get('.lsf-relations').contains(from).closest('.lsf-relations').contains(to); + /** + * Check that relation exists + * @param relationArgs + */ + hasRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs).should('be.visible'); + }, + /** + * Check that relation has specific direction + * @param direction + * @param relationArgs + */ + hasRelationDirection(direction: Direction, ...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find(`[data-direction="${direction}"]`) + .should('be.visible'); + }, + /** + * Check that relation has specific labels + * @param labels + * @param relationArgs + */ + hasRelationLabels(labels: string | string[], ...relationArgs: RelationArgs) { + if (!Array.isArray(labels)) { + labels = [labels]; + } + + const $selector = this.getRelation(...relationArgs) + .find('.lsf-relation-meta .ant-select-selection-overflow'); + + if (labels.length === 0) { + // There is also input in the selector + $selector.children().should('have.length', 1); + return; + } + + for (const label of labels) { + this.getRelation(...relationArgs).find(`.ant-select-selection-item[title="${label}"]`).should('be.visible'); + } + }, + addLabelToRelation(label: string, ...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('.lsf-relation-meta .ant-select-selector') + .click() + .find('.ant-select-selection-search-input') + .type(label) + .type('{enter}'); + + cy.get('body').click(); + }, + /** + * Check that relation is hidden + * @param relationArgs + */ + isHiddenRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs).should('have.class', 'lsf-relations__item_hidden'); + }, + /** + * Check that relation is not hidden + * @param relationArgs + */ + isNotHiddenRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs).should('not.have.class', 'lsf-relations__item_hidden'); + }, + /** + * Hover over relation to show action buttons + * @param relationArgs + */ + hoverOverRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs).trigger('mouseover'); + }, + /** + * Stop hovering over relation to hide action buttons + * @param relationArgs + */ + stopHoveringOverRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs).trigger('mouseout'); + }, + /** + * Toggle relation direction + * @param relationArgs + */ + toggleRelationDirection(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('.lsf-relations__direction') + .parent() + .click(); + }, + /** + * Click delete relation button + * @param relationArgs + */ + clickDelete(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('[aria-label="Delete Relation"]') + .click(); + }, + /** + * Click show relation button + * @param relationArgs + */ + clickShowRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('[aria-label="Show Relation"]') + .click(); + }, + /** + * Click hide relation button + * @param relationArgs + */ + clickHideRelation(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('[aria-label="Hide Relation"]') + .click(); + }, + /** + * Click show relation labels button + * @param relationArgs + */ + clickShowRelationLabels(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('[aria-label="Show Relation Labels"]') + .click(); + }, + /** + * Click hide relation labels button + * @param relationArgs + */ + clickHideRelationLabels(...relationArgs: RelationArgs) { + this.getRelation(...relationArgs) + .find('[aria-label="Hide Relation Labels"]') + .click(); + }, + /** + * Action that deletes relation without additional preparations + * @param relationArgs + */ + deleteRelationAction(...relationArgs: RelationArgs) { + this.hoverOverRelation(...relationArgs); + this.clickDelete(...relationArgs); + }, + /** + * Action that hides relation without additional preparations + * @param relationArgs + */ + hideRelationAction(...relationArgs: RelationArgs) { + this.hoverOverRelation(...relationArgs); + this.clickHideRelation(...relationArgs); + this.stopHoveringOverRelation(...relationArgs); + }, + /** + * Action that shows relation without additional preparations + * @param relationArgs + */ + showRelationAction(...relationArgs: RelationArgs) { + this.hoverOverRelation(...relationArgs); + this.clickShowRelation(...relationArgs); + this.stopHoveringOverRelation(...relationArgs); }, + /** + * Toggle relation creation mode by button + */ toggleCreation() { cy.get('.lsf-region-actions__group_align_left > :nth-child(1) > .lsf-button__icon').click(); }, + /** + * Toggle relation creation mode by hotkey + */ toggleCreationWithHotkey() { // hotkey is alt + r cy.get('body').type('{alt}r'); diff --git a/helpers/LSF/ToolBar.ts b/helpers/LSF/ToolBar.ts index e41f83f..6c20138 100644 --- a/helpers/LSF/ToolBar.ts +++ b/helpers/LSF/ToolBar.ts @@ -1,10 +1,62 @@ export const ToolBar = { - get root() { + /** + * Represents the root HTML element. + * @returns {Cypress.Chainable>} Cypress object which represents the root HTML element. + */ + get root(): Cypress.Chainable> { return cy.get('.lsf-topbar'); }, - - get submitBtn() { + /** + * Represents the submit button HTML element. + * @returns {Cypress.Chainable>} Cypress object which represents the submit button HTML element. + */ + get submitBtn(): Cypress.Chainable> { return this.root .find('[aria-label="submit"]'); }, + /** + * Represents the annotations dropdown toggle HTML element. + * @returns {Cypress.Chainable>} Cypress object which represents the annotations toggle HTML element. + */ + get annotationsToggle(): Cypress.Chainable> { + return this.root + .find('.lsf-annotations-list'); + }, + /** + * Represents the create annotation button HTML element. + * @returns {Cypress.Chainable>} Cypress object which represents the create annotation button HTML element. + */ + get createAnnotationButton(): Cypress.Chainable> { + return this.root + .find('.lsf-annotations-list__create'); + }, + /** + * Represents the list of annotation HTML elements in the dropdown. + * @returns {Cypress.Chainable>} Cypress object which represents the list of annotation HTML elements in the dropdown. + */ + get annotationsList(): Cypress.Chainable> { + return this.root.find('.lsf-annotations-list__list'); + }, + /** + * Toggles the display of the annotations list. + */ + toggleAnnotationsList(): void { + this.annotationsToggle.click(); + }, + /** + * Triggers the creation of a new annotation. + */ + createNewAnnotation(): void { + this.createAnnotationButton.click(); + }, + /** + * Triggers a click event on a specific annotation in the list. + * @param annotationIndex {number} - The index of the annotation in the list. + */ + selectAnnotation(annotationIndex: number): void { + this.annotationsList + .find('.lsf-annotations-list__entity') + .eq(annotationIndex) + .click(); + }, }; diff --git a/tsconfig.json b/tsconfig.json index 9397f8c..b8b0e96 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,5 +7,8 @@ "types": ["cypress", "node"], "typeRoots": ["./types", "./node_modules/@types"] }, - "include": ["**/*.ts"] + "include": [ + "**/*.ts", + "node_modules/cypress/types/jquery/JQuery.d.ts" + ] } diff --git a/types/Cypress.d.ts b/types/Cypress.d.ts index 31c62e5..6b436fb 100644 --- a/types/Cypress.d.ts +++ b/types/Cypress.d.ts @@ -1,3 +1,4 @@ +/// declare namespace Cypress { interface Tresholdable { treshold?: number; @@ -5,7 +6,7 @@ declare namespace Cypress { interface CompareScreenshotOptions extends ScreenshotOptions { withHidden: string[]; } - interface Chainable { + interface Chainable { /** * Custom command to select DOM element by data-cy attribute. * @example cy.dataCy('greeting')