import { cloneDeep } from 'lodash-es' const username = Cypress.env('username') const password = Cypress.env('password') const hostUrl = Cypress.env('hosturl') const appLocation = Cypress.env('appLocation') const longerCommandTimeout = Cypress.env('longerCommandTimeout') const serverType = Cypress.env('serverType') const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`) context('editor tests: ', function () { this.beforeAll(() => { cy.visit(`${hostUrl}/SASLogon/logout`) cy.loginAndUpdateValidKey(true) }) this.beforeEach(() => { cy.visit(hostUrl + appLocation) cy.wait(2000) cy.get('body').then(($body) => { const usernameInput = $body.find('input.username')[0] if (usernameInput && !Cypress.dom.isHidden(usernameInput)) { cy.get('input.username').type(username) cy.get('input.password').type(password) cy.get('.login-group button').click() } }) visitPage('home') }) it('1 | Add one viewbox', (done) => { const viewbox_table = 'mpe_audit' const columns = ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) checkColumns(columns, () => { done() }) }) it('2 | Add two viewboxes', (done) => { const viewboxes = [ { viewbox_table: 'mpe_audit', columns: ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'] }, { viewbox_table: 'mpe_alerts', columns: [ 'TX_FROM', 'ALERT_EVENT', 'ALERT_LIB', 'ALERT_DS', 'ALERT_USER' ] } ] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree( libraryToOpenIncludes, viewboxes.map((viewbox) => viewbox.viewbox_table) ) viewboxes.forEach((viewbox) => { cy.contains('.open-viewbox', viewbox.viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) }) let result = 0 viewboxes.forEach((viewbox) => { checkColumns(viewbox.columns, () => { result++ if (result === viewboxes.length) done() }) }) }) it('3 | Add viewbox, add columns', (done) => { const viewbox_table = 'mpe_audit' const columns = ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'] const additionalColumns = ['IS_PK'] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) openViewboxConfig(viewbox_table) removeAllColumns() addColumns(additionalColumns) checkColumns([...columns, ...additionalColumns], () => { done() }) }) it('4 | Add viewbox, add columns and reorder', (done) => { const viewbox_table = 'mpe_audit' const columns = ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'] const additionalColumns = ['IS_PK', 'MOVE_TYPE'] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) openViewboxConfig(viewbox_table) removeAllColumns() addColumns(additionalColumns, () => { cy.wait(1000) //reorder cy.get('.col-box.column-MOVE_TYPE') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) cy.wait(200) // In our case, we wait 200ms cause we have animations which we are sure that take this amount of time cy.get('.col-box.column-IS_PK') .realMouseMove(0, 0, { position: 'center' }) .realMouseUp() //reorder end cy.wait(500) checkColumns([...columns, ...additionalColumns.reverse()], () => { done() }) }) }) it('5 | Add viewbox, add columns, reorder, remove column, add again', (done) => { const viewbox_table = 'mpe_audit' const columns = ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'] const additionalColumns = ['IS_PK', 'MOVE_TYPE'] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) openViewboxConfig(viewbox_table) removeAllColumns() addColumns(additionalColumns, () => { cy.wait(1000) //reorder cy.get('.col-box.column-MOVE_TYPE') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) cy.wait(200) // In our case, we wait 200ms cause we have animations which we are sure that take this amount of time cy.get('.col-box.column-IS_PK') .realMouseMove(0, 0, { position: 'center' }) .realMouseUp() //reorder end cy.wait(500) checkColumns([...columns, ...additionalColumns.reverse()], () => { const colToRemove = 'MOVE_TYPE' removeColumn(colToRemove) checkColumns( [ ...columns, ...additionalColumns.filter((col) => col !== colToRemove) ], () => { addColumns([colToRemove], () => { checkColumns([...columns, ...additionalColumns.reverse()], () => { done() }) }) } ) }) }) }) it('6 | Add viewboxes, reload and check url restored configuration', (done) => { const viewboxes = [ { viewbox_table: 'mpe_audit', columns: ['LOAD_REF', 'LIBREF', 'DSN', 'KEY_HASH', 'TGTVAR_NM'], additionalColumns: ['IS_PK', 'MOVE_TYPE'] }, { viewbox_table: 'mpe_alerts', columns: [ 'TX_FROM', 'ALERT_EVENT', 'ALERT_LIB', 'ALERT_DS', 'ALERT_USER' ], additionalColumns: ['TX_TO'] } ] openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [ viewboxes[0].viewbox_table, viewboxes[1].viewbox_table ]) openViewboxConfig(viewboxes[0].viewbox_table) cy.wait(500) removeAllColumns() addColumns(viewboxes[0].additionalColumns, () => { cy.wait(1000) if (viewboxes[0].viewbox_table === 'mpe_audit') { cy.get('.col-box.column-MOVE_TYPE') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) cy.wait(200) // In our case, we wait 200ms cause we have animations which we are sure that take this amount of time cy.get('.col-box.column-IS_PK') .realMouseMove(0, 0, { position: 'center' }) .realMouseUp() } cy.wait(1000) openViewboxConfig(viewboxes[1].viewbox_table) addColumns(viewboxes[1].additionalColumns, () => { cy.wait(1000).reload() let result = 0 checkColumns( [ ...viewboxes[0].columns, ...cloneDeep(viewboxes[0].additionalColumns.reverse()) ], () => { result++ if (result === 2) done() } ) checkColumns( [...viewboxes[1].columns, ...viewboxes[1].additionalColumns], () => { result++ if (result === 2) done() } ) }) }) }) // The base VIEW table and a viewbox on a different table must never // share a filter dialog's cached state - each keeps its own entry in // the globals filter cache (getFilterObjPath), keyed by viewboxId where // one is present. Opened from the VIEW page specifically // (rootParam === 'view') - the one branch where this previously broke, // since a 'view'-page QueryComponent was always treated as the base // table regardless of viewboxId. it('7 | Base table filter and viewbox filter stay isolated from each other', () => { const viewbox_table = 'mpe_audit' visitPage('view/data') openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') // Filter the base table. openViewFilter() setFilterWithValue('SOME_CHAR', 'this is dummy data', 'value', () => { cy.get('app-query', { withinSubject: null }).should('not.exist') // Open a viewbox on a *different* table and check its filter dialog // does not show the base table's filter. openViewViewboxes() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) closeViewboxModal() // cy.contains retries until the viewbox's title actually shows the // expected table name (it can render before its data/title loads), // then .parents() walks back up to its container - avoids a // one-shot .then() racing the title's own async update, which is // what made this flaky in the first place. cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .within(() => { cy.get('clr-icon[shape="filter"]').click() cy.get('.filter-modal code.language-sql', { withinSubject: null }).should(($code) => { expect($code.text()).to.not.contain('SOME_CHAR') }) // Reverse direction: filter the viewbox, then confirm // re-opening the base table's own filter dialog still shows // the base table's filter, not the viewbox's. setFilterWithValue('LIBREF', 'sasdemo', 'value', () => { cy.get('app-query', { withinSubject: null }).should('not.exist') openViewFilter() cy.get('.filter-modal code.language-sql', { withinSubject: null }).should(($code) => { expect($code.text()).to.contain('SOME_CHAR') expect($code.text()).to.not.contain('LIBREF') }) }) }) }) }) it('8 | Dragging a viewbox by its corner handle resizes it', () => { const viewbox_table = 'mpe_audit' openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) // The Viewboxes picker modal is still open at this point, its backdrop // sitting above .viewboxes-container - a real click (unlike cy.find(), // which doesn't care what's visually on top) would land on the modal // instead of the handle underneath it, silently no-opping the drag. closeViewboxModal() // Re-query by the title text rather than caching a reference, since // the viewbox can still be re-rendering its content right after // opening. cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .as('viewboxEl') // tableOnClick() calls snapToGrid() asynchronously right after its data // load resolves, which overwrites width/height/x/y for every open // viewbox via the [style.width.px]/[style.height.px] bindings - totally // independent of any drag. The table title above renders immediately // (before that data load finishes), so capturing initialRect right // after it appears races snapToGrid: the box can still silently resize // itself out from under the test between capturing initialRect and the // drag. Waiting for the table's own header cells (populated only once // that same data load - and so snapToGrid - has completed) closes that // race, same signal checkColumns() uses elsewhere in this file. cy.get('@viewboxEl').find('.ht_master.handsontable thead tr th', { timeout: longerCommandTimeout }) cy.get('@viewboxEl').then(($box) => { const initialRect = $box[0].getBoundingClientRect() // Chained off one query, not re-queried between each step, so // cypress-real-events keeps operating on the same subject throughout. // // Dragging INWARD (negative offsets, shrinking) rather than outward: // a freshly opened viewbox starts flush against the right edge of the // viewport (x: window.innerWidth - defaultConfig.width, so // box.right === innerWidth exactly). realMouseMove's target // coordinate is computed as an absolute point on the page, so dragging // the corner outward/rightward to grow the box would target a point // past the viewport's right edge, outside the Cypress AUT iframe // entirely - Chrome never delivers a move event for a point outside // the iframe. Shrinking stays well within the box's own footprint // (500x300 default, 200x200 min per .viewbox's CSS), so the target // coordinate is always safely on-screen - same resize() code path, // just exercised in the direction that's actually simulatable. cy.get('@viewboxEl') .find('.dragHandle.corner') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(-100, -80, { position: 'topLeft' }) .realMouseUp() // A generous threshold, not an exact pixel match - real rendering has // its own small offsets (see setHandleTransform's "fine tune" +5). // The point is confirming the box actually resizes at all in response // to the drag. cy.get('@viewboxEl').should(($box) => { const rect = $box[0].getBoundingClientRect() expect(rect.width, 'width after resize').to.be.lessThan( initialRect.width - 30 ) expect(rect.height, 'height after resize').to.be.lessThan( initialRect.height - 30 ) }) }) }) it('9 | Dragging a viewbox by its right-edge handle resizes width only', () => { const viewbox_table = 'mpe_audit' openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) closeViewboxModal() cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .as('viewboxEl') // See test 8's comment: snapToGrid() resizes every open viewbox // asynchronously right after its data load resolves, racing initialRect // capture if that happens right after the title (which renders before // the load finishes) appears. Wait for the table's own header cells // first, since those only populate once that same load has completed. cy.get('@viewboxEl').find('.ht_master.handsontable thead tr th', { timeout: longerCommandTimeout }) cy.get('@viewboxEl').then(($box) => { const initialRect = $box[0].getBoundingClientRect() // Chained off one query - see test 8's comment on why re-querying // between down/move/up silently drops the move. Dragging INWARD // (negative offset, shrinking) rather than outward - see test 8's // comment: a freshly opened viewbox starts flush against the // viewport's right edge, so growing rightward would target a point // past the AUT iframe's edge, which Chrome never delivers a move // event for. cy.get('@viewboxEl') .find('.dragHandle.right') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(-100, 0, { position: 'topLeft' }) .realMouseUp() cy.get('@viewboxEl').should(($box) => { const rect = $box[0].getBoundingClientRect() expect(rect.width, 'width after resize').to.be.lessThan( initialRect.width - 30 ) // This handle must only affect width - height (and the box's own // left edge) stay put. expect(rect.height, 'height unchanged').to.be.closeTo( initialRect.height, 5 ) expect(rect.left, 'left edge unchanged').to.be.closeTo( initialRect.left, 5 ) }) }) }) it('10 | Dragging a viewbox by its bottom-left corner handle resizes it and moves its left edge', () => { const viewbox_table = 'mpe_audit' openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) closeViewboxModal() cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .as('viewboxEl') // See test 8's comment: wait for snapToGrid()'s async resize to settle // before capturing initialRect, or the box can silently change size out // from under the test. cy.get('@viewboxEl').find('.ht_master.handsontable thead tr th', { timeout: longerCommandTimeout }) cy.get('@viewboxEl').then(($box) => { const initialRect = $box[0].getBoundingClientRect() // Chained off one query - see test 8's comment on why re-querying // between down/move/up silently drops the move. Dragging this // handle right+down (only positive offsets are meaningful for // realMouseMove's position-anchored coordinates) shrinks width // while moving the box's own left edge right by the same amount, // and grows height - unlike every other handle here, this one is // expected to move the box (viewbox.x) as well as resize it, which // is the more error-prone half of this handler to cover. cy.get('@viewboxEl') .find('.dragHandle.corner-left') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(60, 80, { position: 'topLeft' }) .realMouseUp() cy.get('@viewboxEl').should(($box) => { const rect = $box[0].getBoundingClientRect() expect(rect.width, 'width after resize').to.be.lessThan( initialRect.width - 30 ) expect(rect.left, 'left edge moved right').to.be.greaterThan( initialRect.left + 30 ) expect(rect.height, 'height after resize').to.be.greaterThan( initialRect.height + 30 ) }) }) }) it('11 | Dragging a viewbox by its left-edge handle resizes width and moves its left edge, leaving height alone', () => { const viewbox_table = 'mpe_audit' openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) closeViewboxModal() cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .as('viewboxEl') // See test 8's comment: wait for snapToGrid()'s async resize to settle // before capturing initialRect, or the box can silently change size out // from under the test. cy.get('@viewboxEl').find('.ht_master.handsontable thead tr th', { timeout: longerCommandTimeout }) cy.get('@viewboxEl').then(($box) => { const initialRect = $box[0].getBoundingClientRect() // Chained off one query - see test 8's comment on why re-querying // between down/move/up silently drops the move. Dragging this // handle right (only positive offsets are meaningful for // realMouseMove's position-anchored coordinates) shrinks width // while moving the box's own left edge right by the same amount - // same viewbox.x-moving behavior as the bottom-left corner, minus // the height change. cy.get('@viewboxEl') .find('.dragHandle.left') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(60, 0, { position: 'topLeft' }) .realMouseUp() cy.get('@viewboxEl').should(($box) => { const rect = $box[0].getBoundingClientRect() expect(rect.width, 'width after resize').to.be.lessThan( initialRect.width - 30 ) expect(rect.left, 'left edge moved right').to.be.greaterThan( initialRect.left + 30 ) expect(rect.height, 'height unchanged').to.be.closeTo( initialRect.height, 5 ) }) }) }) it('12 | Dragging a viewbox by its bottom-edge handle resizes height only', () => { const viewbox_table = 'mpe_audit' openTableFromTree(libraryToOpenIncludes, 'mpe_x_test') cy.get('.viewbox-open').click() openTableFromViewboxTree(libraryToOpenIncludes, [viewbox_table]) cy.contains('.open-viewbox', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) closeViewboxModal() cy.contains('.viewboxes-container .viewbox .table-title', viewbox_table, { matchCase: false, timeout: longerCommandTimeout }) .parents('.viewbox') .first() .as('viewboxEl') // See test 8's comment: wait for snapToGrid()'s async resize to settle // before capturing initialRect, or the box can silently change size out // from under the test. cy.get('@viewboxEl').find('.ht_master.handsontable thead tr th', { timeout: longerCommandTimeout }) cy.get('@viewboxEl').then(($box) => { const initialRect = $box[0].getBoundingClientRect() // Chained off one query - see test 8's comment on why re-querying // between down/move/up silently drops the move. cy.get('@viewboxEl') .find('.dragHandle.bottom') .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 100, { position: 'topLeft' }) .realMouseUp() cy.get('@viewboxEl').should(($box) => { const rect = $box[0].getBoundingClientRect() expect(rect.height, 'height after resize').to.be.greaterThan( initialRect.height + 30 ) // This handle must only affect height - width (and the box's own // left edge) stay put. expect(rect.width, 'width unchanged').to.be.closeTo( initialRect.width, 5 ) expect(rect.left, 'left edge unchanged').to.be.closeTo( initialRect.left, 5 ) }) }) }) }) const removeAllColumns = () => { cy.get('.configuration-wrapper clr-icon[shape="trash"]').then( (removeNodes) => { for (let removeNode of removeNodes) { removeNode.click() } } ) } // Asserts that at least one open viewbox currently shows exactly this // column list, in order. Uses a single .should() callback (synchronous // jQuery reads only, no nested cy.get()/.then()) so Cypress retries the // whole check against a fresh DOM snapshot until it passes or times out - // unlike .then(), which only ever sees one snapshot and, combined with an // early return on a mismatch, would silently never invoke the caller's // callback while Handsontable/the viewbox's data are still loading or // re-rendering asynchronously. const checkColumns = (columns: string[], callback: () => void) => { cy.get('.viewboxes-container .viewbox', { withinSubject: null }).should( ($viewboxNodes) => { const matches = $viewboxNodes.toArray().some((viewboxNode) => { // rowHeaders: true (viewboxes.component.ts) adds a leading, unlabeled // corner before the actual data columns - skip it, or every // comparison is off by one against the row-header cell instead of // the first real column. const headerCells = Cypress.$(viewboxNode) .find('.ht_master.handsontable thead tr th') .toArray() .slice(1) return columns.every((col, i) => (headerCells[i]?.innerHTML || '') .toLowerCase() .includes(col.toLowerCase()) ) }) expect(matches, `a viewbox showing columns: ${columns.join(', ')}`).to.be .true } ) cy.then(() => callback()) } const closeViewboxModal = () => { cy.get('app-viewboxes .close', { withinSubject: null }).click() } const removeColumn = (column: string) => { cy.get(`.col-box.column-${column} clr-icon`, { withinSubject: null }).click() } const addColumns = (columns: string[], callback?: () => void) => { for (const column of columns) { cy.get('.cols-search input', { withinSubject: null }).type(column) cy.get('.cols-search .autocomplete-wrapper', { withinSubject: null }) .first() .trigger('keydown', { key: 'ArrowDown' }) cy.get('.cols-search .autocomplete-wrapper', { withinSubject: null }) .first() .trigger('keydown', { key: 'Enter' }) // Wait for the actual column box to render before moving on to the // next column (or firing the callback) - selecting it doesn't add it // to the DOM synchronously, and cy.get()'s own retry covers that gap // instead of relying on a fixed cy.wait(). cy.get(`.col-box.column-${column}`, { timeout: longerCommandTimeout }) } if (callback) cy.then(() => callback()) } // The VIEW page's "options" dropdown (viewer.component.html) - distinct // from the editor page's dedicated .viewbox-open button used by the // tests above, since test 7 runs on the VIEW page specifically. const openViewOptionsDropdown = () => { cy.get('.filterSide', { withinSubject: null }).click() } const openViewFilter = () => { openViewOptionsDropdown() cy.get('[clrDropdownItem]', { withinSubject: null }) .contains('Filter') .click() } const openViewViewboxes = () => { openViewOptionsDropdown() cy.get('[clrDropdownItem]', { withinSubject: null }) .contains('Viewboxes') .click() } // cy.contains retries until a matching node appears/updates - the open // viewbox's own list entry can render before its table-name text has // actually loaded, so a one-shot .then() lookup here would race it. const openViewboxConfig = (viewbox_tablename: string) => { cy.contains('.open-viewbox', viewbox_tablename, { matchCase: false, timeout: longerCommandTimeout }).click() } const openTableFromTree = (libNameIncludes: string, tablename: string) => { cy.get('.app-loading', { timeout: longerCommandTimeout }).should('not.exist') // Small settle wait: right after a fresh visit (an extra navigation on // top of beforeEach's own, e.g. test 7's visitPage('view/data')), the // tree component can still be re-rendering, causing a node to be found // then swapped out mid-click - same fix already proven in // viewer-labels.cy.ts. cy.wait(300) const libraryNodeSelector = '.nav-tree clr-tree > clr-tree-node' const libraryMatcher = new RegExp(libNameIncludes, 'i') cy.contains(libraryNodeSelector, libraryMatcher, { timeout: longerCommandTimeout }) .find('.clr-tree-node-content-container p') .click() // Re-query the library node fresh rather than reusing the reference // from before the click above - expanding a library can replace its // whole DOM subtree once its child tables load (that's what made the // earlier cached-reference version of this helper flaky: Cypress // detected the click's own side effect detaching the element it was // still verifying actionability against). cy.contains(libraryNodeSelector, libraryMatcher, { timeout: longerCommandTimeout }).within(() => { cy.contains('.clr-treenode-link', tablename, { matchCase: false, timeout: longerCommandTimeout }).click() }) } const setFilterWithValue = ( variableValue: string, valueString: string, valueField: 'value' | 'time' | 'date' | 'datetime' | 'in' | 'between', callback?: any ) => { cy.wait(600) cy.focused().type(variableValue) cy.wait(100) // cy.focused().trigger('input') cy.get('.variable-col .autocomplete-wrapper', { withinSubject: null }) .first() .trigger('keydown', { key: 'ArrowDown' }) cy.get('.variable-col .autocomplete-wrapper', { withinSubject: null }).trigger('keydown', { key: 'Enter' }) cy.focused().tab() cy.wait(100) if (valueField === 'in') { cy.focused().select(valueField.toUpperCase()).trigger('change') } else if (valueField === 'between') { cy.focused().select(valueField.toUpperCase()).trigger('change') } else { cy.focused().tab() cy.wait(100) } switch (valueField) { case 'value': { cy.focused().type(valueString) break } case 'time': { cy.focused().type(valueString) break } case 'date': { cy.focused().type(valueString) cy.focused().tab() break } case 'datetime': { const date = valueString.split(' ')[0] const time = valueString.split(' ')[1] cy.focused().type(date) cy.focused().tab() cy.focused().tab() cy.focused().type(time) break } case 'in': { cy.get('.checkbox-vals').then(() => { cy.focused().tab() cy.focused().click() cy.get('.no-values') .should('not.exist') .then(() => { cy.get('clr-checkbox-wrapper input').then((inputs: any) => { inputs[0].click() cy.get('.in-values-modal .modal-footer button').click() cy.get('.modal-footer .btn-success-outline').click() if (callback) callback() }) }) }) break } case 'between': { cy.focused().tab() const start = valueString.split('-')[0] const end = valueString.split('-')[1] cy.focused().type(start) cy.focused().tab() cy.focused().type(end) } default: { break } } cy.wait(100) cy.focused().tab() cy.wait(100) cy.focused().tab() cy.wait(100) cy.focused().tab() cy.wait(100) cy.focused().tab() cy.wait(100) cy.focused().click() if (callback) callback() } const openTableFromViewboxTree = ( libNameIncludes: string, tablenames: string[] ) => { cy.get('.add-new clr-tree > clr-tree-node', { timeout: longerCommandTimeout }).then((treeNodes: any) => { let viyaLib for (let node of treeNodes) { if (node.innerText.toLowerCase().includes(libNameIncludes)) { viyaLib = node break } } cy.get(viyaLib).within(() => { cy.get('p') .click() .then(() => { cy.get('.clr-treenode-link').then(async (innerNodes: any) => { for (let innerNode of innerNodes) { for (let tablename of tablenames) { await pause(300) if (innerNode.innerText.toLowerCase().includes(tablename)) { innerNode.click() } } } }) }) }) }) } const pause = (ms: number) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(null) }, ms) }) } const visitPage = (url: string) => { cy.visit(`${hostUrl}${appLocation}/#/${url}`) }