Skip to content
Snippets Groups Projects
Commit 3f0e3ab8 authored by Xiao Gui's avatar Xiao Gui
Browse files

chore: clean up e2e helper class

parent 74b11328
No related branches found
No related tags found
No related merge requests found
......@@ -3,7 +3,7 @@ const { width, height } = require('./opts')
module.exports = [
...(process.env.DISABLE_CHROME_HEADLESS ? [] : ['--headless']),
'--no-sandbox',
'--disable-gpu',
...(process.env.ENABLE_GPU ? []: ['--disable-gpu']),
'--disable-setuid-sandbox',
"--disable-extensions",
`--window-size=${width},${height}`,
......
const { AtlasPage } = require('../util')
const { ARIA_LABELS } = require('../../../common/constants')
const { CONST } = require('../../../common/constants')
const { retry } = require('../../../common/util')
const { TOGGLE_EXPLORE_PANEL, MODALITY_FILTER, DOWNLOAD_PREVIEW, DOWNLOAD_PREVIEW_CSV } = ARIA_LABELS
const atlasName = `Multilevel Human Atlas`
......@@ -110,7 +109,7 @@ describe('> dataset browser', () => {
await iavPage.selectSearchRegionAutocompleteWithText()
await retry(async () => {
await iavPage.dismissModal()
await iavPage._setRegionalFeaturesExpanded(true)
await iavPage.toggleExpansionPanelState(`${CONST.REGIONAL_FEATURES}`)
}, {
timeout: 2000,
retries: 10
......@@ -126,153 +125,3 @@ describe('> dataset browser', () => {
})
}
})
const template = 'ICBM 2009c Nonlinear Asymmetric'
const area = 'Area hOc1 (V1, 17, CalcS)'
const receptorName = `Density measurements of different receptors for Area hOc1 (V1, 17, CalcS) [human, v1.0]`
// describe('> receptor dataset previews', () => {
// let iavPage
// beforeEach(async () => {
// iavPage = new AtlasPage()
// await iavPage.init()
// await iavPage.goto()
// await iavPage.selectTitleCard(template)
// await iavPage.wait(500)
// await iavPage.waitUntilAllChunksLoaded()
// await iavPage.searchRegionWithText(area)
// await iavPage.wait(2000)
// await iavPage.selectSearchRegionAutocompleteWithText()
// await iavPage.dismissModal()
// await iavPage.searchRegionWithText('')
// const datasets = await iavPage.getVisibleDatasets()
// const receptorIndex = datasets.indexOf(receptorName)
// await iavPage.clickNthDataset(receptorIndex)
// await iavPage.wait(500)
// await iavPage.click(`[aria-label="${ARIA_LABELS.SHOW_DATASET_PREVIEW}"]`)
// await iavPage.waitFor(true, true)
// })
// describe('> can display graph', () => {
// it('> can display radar graph', async () => {
// const files = await iavPage.getBottomSheetList()
// const fingerprintIndex = files.findIndex(file => /fingerprint/i.test(file))
// await iavPage.clickNthItemFromBottomSheetList(fingerprintIndex)
// await iavPage.waitFor(true, true)
// const modalHasCanvas = await iavPage.modalHasChild('canvas')
// expect(modalHasCanvas).toEqual(true)
// await iavPage.wait(500)
// const modalHasDownloadBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW}"]`)
// const modalHasDownloadCSVBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW_CSV}"]`)
// expect(modalHasDownloadBtn).toEqual(true)
// expect(modalHasDownloadCSVBtn).toEqual(true)
// })
// it('> can display profile', async () => {
// const files = await iavPage.getBottomSheetList()
// const profileIndex = files.findIndex(file => /profile/i.test(file))
// await iavPage.clickNthItemFromBottomSheetList(profileIndex)
// await iavPage.waitFor(true, true)
// const modalHasCanvas = await iavPage.modalHasChild('canvas')
// expect(modalHasCanvas).toEqual(true)
// await iavPage.wait(500)
// const modalHasDownloadBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW}"]`)
// const modalHasDownloadCSVBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW_CSV}"]`)
// expect(modalHasDownloadBtn).toEqual(true)
// expect(modalHasDownloadCSVBtn).toEqual(true)
// })
// })
// it('> can display image', async () => {
// const files = await iavPage.getBottomSheetList()
// const imageIndex = files.findIndex(file => /image\//i.test(file))
// await iavPage.clickNthItemFromBottomSheetList(imageIndex)
// await iavPage.wait(500)
// const modalHasImage = await iavPage.modalHasChild('div[data-img-src]')
// expect(modalHasImage).toEqual(true)
// await iavPage.wait(500)
// const modalHasDownloadBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW}"]`)
// const modalHasDownloadCSVBtn = await iavPage.modalHasChild(`[aria-label="${DOWNLOAD_PREVIEW_CSV}"]`)
// expect(modalHasDownloadBtn).toEqual(true)
// expect(modalHasDownloadCSVBtn).toEqual(false)
// })
// })
// describe('> modality picker', () => {
// let iavPage
// beforeAll(async () => {
// iavPage = new AtlasPage()
// await iavPage.init()
// await iavPage.goto()
// })
// it('> sorted alphabetically', async () => {
// await iavPage.selectTitleCard(templates[1])
// await iavPage.wait(500)
// await iavPage.waitUntilAllChunksLoaded()
// await iavPage.click(`[aria-label="${TOGGLE_EXPLORE_PANEL}"]`)
// await iavPage.wait(500)
// await iavPage.clearAlerts()
// await iavPage.click(`[aria-label="${MODALITY_FILTER}"]`)
// await iavPage.wait(500)
// const modalities = await iavPage.getModalities()
// for (let i = 1; i < modalities.length; i ++) {
// expect(
// modalities[i].charCodeAt(0)
// ).toBeGreaterThanOrEqual(
// modalities[i - 1].charCodeAt(0)
// )
// }
// })
// })
// describe('> pmap dataset preview', () => {
// let iavPage
// beforeAll(async () => {
// // loads pmap and centers on hot spot
// const url = `/?templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..dABI~.525x0~.7iMV..1EPC&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FVisual_hOc1_l_N10_nlin2MNI152ASYM2009C_2.4_publicP_d3045ee3c0c4de9820eb1516d2cc72bb.nii.gz&previewingDatasetFiles=%5B%7B"datasetId"%3A"minds%2Fcore%2Fdataset%2Fv1.0.0%2F5c669b77-c981-424a-858d-fe9f527dbc07"%2C"filename"%3A"Area+hOc1+%28V1%2C+17%2C+CalcS%29+%5Bv2.4%2C+ICBM+2009c+Asymmetric%2C+left+hemisphere%5D"%7D%5D`
// iavPage = new AtlasPage()
// await iavPage.init()
// await iavPage.goto(url)
// await iavPage.waitUntilAllChunksLoaded()
// })
// it('> can display pmap', async () => {
// const { red, green, blue } = await iavPage.getRgbAt({position: [200, 597]})
// expect(red).toBeGreaterThan(green)
// expect(red).toBeGreaterThan(blue)
// })
// it('> on update of layer control, pmap retains', async () => {
// // by default, additional layer control is collapsed
// // await iavPage.toggleLayerControl() // deprecated
// await iavPage.wait(500)
// await iavPage.toggleNthLayerControl(0)
// await iavPage.wait(5500)
// // interact with control
// await iavPage.click(`[aria-label="Remove background"]`)
// await iavPage.wait(500)
// // color map should be unchanged
// const { red, green, blue } = await iavPage.getRgbAt({position: [200, 597]})
// expect(red).toBeGreaterThan(green)
// expect(red).toBeGreaterThan(blue)
// })
// })
const { AtlasPage } = require('../util')
const { URLSearchParams } = require('url')
const { ARIA_LABELS } = require('../../../common/constants')
describe('> non-atlas images', () => {
let iavPage
......@@ -196,9 +197,15 @@ describe('> non-atlas images', () => {
await iavPage.goto(`/?${searchParam.toString()}`)
await iavPage.wait(2000)
const additionalLayerControlIsShown = await iavPage.additionalLayerControlIsVisible()
expect(additionalLayerControlIsShown).toEqual(false)
try {
const additionalLayerControlIsShown = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.ADDITIONAL_VOLUME_CONTROL}"]`)
expect(additionalLayerControlIsShown).toEqual(false)
} catch (e) {
/**
* error when css querying additional volume control
* expected behaviour, when it is not visible
*/
}
})
......@@ -219,7 +226,7 @@ describe('> non-atlas images', () => {
await iavPage.goto(`/?${searchParam.toString()}`, { forceTimeout: 20000 })
await iavPage.wait(2000)
const additionalLayerCtrlIsExpanded2 = await iavPage.additionalLayerControlIsExpanded()
const additionalLayerCtrlIsExpanded2 = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.ADDITIONAL_VOLUME_CONTROL}"]`)
expect(additionalLayerCtrlIsExpanded2).toEqual(true)
})
......
......@@ -67,7 +67,7 @@ describe('origin dataset pmap', () => {
await iavPage.wait(5000)
await iavPage.waitForAsync()
const additionalLayerControlIsShown = await iavPage.additionalLayerControlIsVisible()
const additionalLayerControlIsShown = await iavPage.isVisible(`[aria-label="${ARIA_LABELS.ADDITIONAL_VOLUME_CONTROL}"]`)
expect(additionalLayerControlIsShown).toEqual(false)
const checked = await iavPage.switchIsChecked(cssSelector)
......
This diff is collapsed.
const {
WdIavPage,
WdLayoutPage,
WdBase,
} = require('./selenium/iav')
module.exports = {
BasePage: WdBase,
LayoutPage: WdLayoutPage,
AtlasPage: WdIavPage
}
const CITRUS_LIGHT_URL = `https://unpkg.com/citruslight@0.1.0/citruslight.js`
const { By, Key, until } = require('selenium-webdriver')
const { retry } = require('../../../common/util')
const chromeOpts = require('../../chromeOpts')
const ATLAS_URL = (process.env.ATLAS_URL || 'http://localhost:3000').replace(/\/$/, '')
function getActualUrl(url) {
return /^http\:\/\//.test(url) ? url : `${ATLAS_URL}/${url.replace(/^\//, '')}`
}
async function polyFillClick(cssSelector){
if (!cssSelector) throw new Error(`click method needs to define a css selector`)
const webEl = this._browser.findElement( By.css(cssSelector) )
try {
await webEl.click()
} catch (e) {
const id = await webEl.getAttribute('id')
const newId = id.replace(/-input$/, '')
await this._browser.findElement(
By.id(newId)
).click()
}
}
const verifyPosition = position => {
if (!position) throw new Error(`cursorGoto: position must be defined!`)
const x = Array.isArray(position) ? position[0] : position.x
const y = Array.isArray(position) ? position[1] : position.y
if (!x) throw new Error(`cursorGoto: position.x or position[0] must be defined`)
if (!y) throw new Error(`cursorGoto: position.y or position[1] must be defined`)
return {
x,
y
}
}
class WdBase{
constructor() {
browser.waitForAngularEnabled(false)
}
get _browser(){
return browser
}
get _driver(){
return this._browser.driver
}
// without image header
// output as b64 png
async takeScreenshot(cssSelector){
if(cssSelector) {
await this._browser.executeAsyncScript(async () => {
const cb = arguments[arguments.length - 1]
const moduleUrl = arguments[0]
const cssSelector = arguments[1]
const el = document.querySelector(cssSelector)
if (!el) throw new Error(`css selector not fetching anything`)
import(moduleUrl)
.then(async m => {
m.citruslight(el)
cb()
})
}, CITRUS_LIGHT_URL, cssSelector)
}
await this.wait(1000)
const result = await this._browser.takeScreenshot()
if (cssSelector) {
await this._browser.executeAsyncScript(async () => {
const cb = arguments[arguments.length - 1]
const moduleUrl = arguments[0]
const cssSelector = arguments[1]
const el = document.querySelector(cssSelector)
if (!el) throw new Error(`css selector not fetching anything`)
import(moduleUrl)
.then(async m => {
m.clearAll()
cb()
})
}, CITRUS_LIGHT_URL, cssSelector)
}
await this.wait(1000)
return result
}
async getRgbAt({ position } = {}, cssSelector = null){
if (!position) throw new Error(`position is required for getRgbAt`)
const { x, y } = verifyPosition(position)
const screenshotData = await this.takeScreenshot(cssSelector)
const [ red, green, blue ] = await this._driver.executeAsyncScript(() => {
const dataUri = arguments[0]
const pos = arguments[1]
const dim = arguments[2]
const cb = arguments[arguments.length - 1]
const img = new Image()
img.onload = () => {
const canvas = document.createElement('canvas')
canvas.width = dim[0]
canvas.height = dim[1]
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
const imgData = ctx.getImageData(0, 0, dim[0], dim[1])
const idx = (dim[0] * pos[1] + pos[0]) * 4
const red = imgData.data[idx]
const green = imgData.data[idx + 1]
const blue = imgData.data[idx + 2]
cb([red, green, blue])
}
img.src = dataUri
}, `data:image/png;base64,${screenshotData}`, [x, y], [800, 796])
return { red, green, blue }
}
async switchIsChecked(cssSelector){
if (!cssSelector) throw new Error(`switchChecked method requies css selector`)
const checked = await this._browser
.findElement( By.css(cssSelector) )
.getAttribute('aria-checked')
return checked === 'true'
}
async click(cssSelector){
return await polyFillClick.bind(this)(cssSelector)
}
async getText(cssSelector){
if (!cssSelector) throw new Error(`getText needs to define css selector`)
const el = await this._browser.findElement( By.css(cssSelector) )
const text = await el.getText()
return text
}
async isVisible(cssSelector) {
if (!cssSelector) throw new Error(`getText needs to define css selector`)
const el = await this._browser.findElement( By.css(cssSelector) )
const isDisplayed = await el.isDisplayed()
return isDisplayed
}
async areVisible(cssSelector){
if (!cssSelector) throw new Error(`getText needs to define css selector`)
const els = await this._browser.findElements( By.css( cssSelector ) )
const returnArr = []
for (const el of els) {
returnArr.push(await el.isDisplayed())
}
return returnArr
}
async isAt(cssSelector){
if (!cssSelector) throw new Error(`getText needs to define css selector`)
const { x, y, width, height } = await this._browser.findElement( By.css(cssSelector) ).getRect()
return { x, y, width, height }
}
async areAt(cssSelector){
if (!cssSelector) throw new Error(`getText needs to define css selector`)
const els = await this._browser.findElements( By.css( cssSelector ) )
const returnArr = []
for (const el of els) {
const { x, y, width, height } = await el.getRect()
returnArr.push({ x, y, width, height })
}
return returnArr
}
historyBack() {
return this._browser.navigate().back()
}
historyForward() {
return this._browser.navigate().forward()
}
async init() {
const wSizeArg = chromeOpts.find(arg => arg.indexOf('--window-size') >= 0)
const [ _, width, height ] = /\=([0-9]{1,})\,([0-9]{1,})$/.exec(wSizeArg)
const newDim = await this._browser.executeScript(async () => {
return [
window.outerWidth - window.innerWidth + (+ arguments[0]),
window.outerHeight - window.innerHeight + (+ arguments[1]),
]
}, width, height)
await this._browser.manage()
.window()
.setRect({
width: newDim[0],
height: newDim[1]
})
}
async waitForAsync(){
const checkReady = async () => {
const els = await this._browser.findElements(
By.css('.spinnerAnimationCircle')
)
const visibleEls = []
for (const el of els) {
if (await el.isDisplayed()) {
visibleEls.push(el)
}
}
return !visibleEls.length
}
do {
await this.wait(500)
} while (
!(await retry(checkReady.bind(this), { timeout: 1000, retries: 10 }))
)
}
async cursorMoveTo({ position }) {
const { x, y } = verifyPosition(position)
return this._driver.actions()
.move()
.move({
x,
y,
duration: 1000
})
.perform()
}
async cursorMoveToElement(cssSelector) {
if (!cssSelector) throw new Error(`cursorMoveToElement needs to define css selector`)
const el = await this._browser.findElement( By.css(cssSelector) )
await this._driver.actions()
.move()
.move({
origin: el,
duration: 1000
})
.perform()
}
async scrollElementBy(cssSelector, options) {
const { delta } = options
await this._browser.executeScript(() => {
const { delta } = arguments[1]
const el = document.querySelector(arguments[0])
el.scrollBy(...delta)
}, cssSelector, { delta })
}
async getScrollStatus(cssSelector) {
const val = await this._browser.executeScript(() => {
const el = document.querySelector(arguments[0])
return el.scrollTop
}, cssSelector)
return val
}
async cursorMoveToAndClick({ position }) {
const { x, y } = verifyPosition(position)
return this._driver.actions()
.move()
.move({
x,
y,
duration: 1000
})
.click()
.perform()
}
async cursorMoveToAndDrag({ position, delta }) {
const { x, y } = verifyPosition(position)
const { x: deltaX, y: deltaY } = verifyPosition(delta)
return this._driver.actions()
.move()
.move({
x,
y,
duration: 1000
})
.press()
.move({
x: x + deltaX,
y: y + deltaY,
duration: 1000
})
.release()
.perform()
}
async initHttpInterceptor(){
await this._browser.executeScript(() => {
if (window.__isIntercepting__) return
window.__isIntercepting__ = true
const open = window.XMLHttpRequest.prototype.open
window.__interceptedXhr__ = []
window.XMLHttpRequest.prototype.open = function () {
window.__interceptedXhr__.push({
method: arguments[0],
url: arguments[1]
})
return open.apply(this, arguments)
}
})
}
async isHttpIntercepting(){
return await this._browser.executeScript(() => {
return window.__isIntercepting__
})
}
async getInterceptedHttpCalls(){
return await this._browser.executeScript(() => {
return window['__interceptedXhr__']
})
}
// it seems if you set intercept http to be true, you might also want ot set do not automat to be true
async goto(url = '/', { interceptHttp, doNotAutomate, forceTimeout = 20 * 1000 } = {}){
this.__trackingNavigationState__ = false
const actualUrl = getActualUrl(url)
if (interceptHttp) {
this._browser.get(actualUrl)
await this.initHttpInterceptor()
} else {
await this._browser.get(actualUrl)
}
// if doNotAutomate is not set
// should wait for async operations to end
if (!doNotAutomate) {
await this.wait(200)
await this.dismissModal()
await this.wait(200)
if (forceTimeout) {
await Promise.race([
this.waitForAsync(),
this.wait(forceTimeout)
])
} else {
await this.waitForAsync()
}
}
}
async wait(ms) {
if (!ms) throw new Error(`wait duration must be specified!`)
return new Promise(rs => {
setTimeout(rs, ms)
})
}
async waitForCss(cssSelector) {
if (!cssSelector) throw new Error(`css selector must be defined`)
await this._browser.wait(
until.elementLocated( By.css(cssSelector) ),
1e3 * 60 * 10
)
}
async waitFor(animation = false, async = false){
if (animation) await this.wait(500)
if (async) await this.waitForAsync()
}
async clearAlerts() {
await this._driver
.actions()
.sendKeys(
Key.ESCAPE,
Key.ESCAPE,
Key.ESCAPE,
Key.ESCAPE
)
.perform()
await this.wait(500)
}
async execScript(fn, ...arg){
const result = await this._driver.executeScript(fn)
return result
}
}
module.exports = {
WdBase
}
const { WdLayoutPage, WdBase } = require('./layout')
const { ARIA_LABELS, CONST } = require('../../../common/constants')
const { _getTextFromWebElement, _getIndexFromArrayOfWebElements } = require('./util')
const { Key } = require('selenium-webdriver')
class WdIavPage extends WdLayoutPage{
constructor(){
super()
}
async clearAllSelectedRegions() {
const clearAllRegionBtn = await this._browser.findElement(
By.css(`[aria-label="${ARIA_LABELS.CLEAR_SELECTED_REGION}"]`)
)
await clearAllRegionBtn.click()
await this.wait(500)
}
async waitUntilAllChunksLoaded(){
await this._browser.wait(async () => {
const els = await this._browser.findElements(
By.css('div.loadingIndicator')
)
const els2 = await this._browser.findElements(
By.css('.spinnerAnimationCircle')
)
return [...els, ...els2].length === 0
}, 1e3 * 60 * 10)
}
async getFloatingCtxInfoAsText(){
const floatingContainer = await this._browser.findElement(
By.css('div[floatingMouseContextualContainerDirective]')
)
const text = await floatingContainer.getText()
return text
}
async selectDropdownTemplate(title) {
throw new Error(`selectDropdownTemplate has been deprecated. use changeTemplate instead`)
}
async _getSearchRegionInput(){
await this._setSideNavPrimaryExpanded(true)
await this.wait(500)
const secondaryOpen = await this._getSideNavSecondaryExpanded()
if (secondaryOpen) {
return this._getSideNavSecondary().findElement( By.css(`[aria-label="${ARIA_LABELS.TEXT_INPUT_SEARCH_REGION}"]`) )
} else {
return this._getSideNavPrimary().findElement( By.css(`[aria-label="${ARIA_LABELS.TEXT_INPUT_SEARCH_REGION}"]`) )
}
}
async searchRegionWithText(text=''){
const searchRegionInput = await this._getSearchRegionInput()
await searchRegionInput
.sendKeys(
Key.chord(Key.CONTROL, 'a'),
text
)
}
async clearSearchRegionWithText() {
const searchRegionInput = await this._getSearchRegionInput()
await searchRegionInput
.sendKeys(
Key.chord(Key.CONTROL, 'a'),
Key.BACK_SPACE,
Key.ESCAPE
)
}
async _getAutcompleteOptions(){
const input = await this._getSearchRegionInput()
const autocompleteId = await input.getAttribute('aria-owns')
const el = await this._browser.findElement( By.css( `[id=${autocompleteId}]` ) )
return this._browser
.findElement( By.id( autocompleteId ) )
.findElements( By.tagName('mat-option') )
}
async getSearchRegionInputAutoCompleteOptions(){
const options = await this._getAutcompleteOptions()
return await Promise.all(
options.map(_getTextFromWebElement)
)
}
async selectSearchRegionAutocompleteWithText(text = ''){
const options = await this._getAutcompleteOptions()
const idx = await _getIndexFromArrayOfWebElements(text, options)
if (idx >= 0) {
await options[idx].click()
} else {
throw new Error(`_getIndexFromArrayOfWebElements ${text} option not founds`)
}
}
_getModalityListView(){
return this._browser
.findElement( By.css('modality-picker') )
.findElements( By.css('mat-checkbox') )
}
async getModalities(){
const els = await this._getModalityListView()
const returnArr = []
for (const el of els) {
returnArr.push(
await el.getText()
)
}
return returnArr
}
_getSingleDatasetListView(){
return this._browser
.findElement( By.css('data-browser') )
.findElements( By.css('single-dataset-list-view') )
}
async getVisibleDatasets() {
const singleDatasetListView = await this._getSingleDatasetListView()
const returnArr = []
for (const item of singleDatasetListView) {
returnArr.push( await item.getText() )
}
return returnArr
}
async clickNthDataset(index){
if (!Number.isInteger(index)) throw new Error(`index needs to be an integer`)
const list = await this._getSingleDatasetListView()
await list[index].click()
}
async togglePinNthDataset(index) {
if (!Number.isInteger(index)) throw new Error(`index needs to be an integer`)
const list = await this._getSingleDatasetListView()
if (!list[index]) throw new Error(`out of bound ${index} in list with length ${list.length}`)
await list[index]
.findElement( By.css('[aria-label="Toggle pinning this dataset"]') )
.click()
}
async viewerIsPopulated() {
try {
const ngContainer = await this._browser.findElement(
By.id('neuroglancer-container')
)
if (! (await ngContainer.isDisplayed())) {
return false
}
const canvas = await ngContainer.findElement(
By.tagName('canvas')
)
if (!(await canvas.isDisplayed())) {
return false
}
return true
} catch (e) {
return false
}
}
async getNavigationState() {
if (!this.__trackingNavigationState__) {
await this._browser.executeScript(async () => {
window.__iavE2eNavigationState__ = {}
const getPr = () => new Promise(rs => {
window.__iavE2eNavigationStateSubptn__ = nehubaViewer.navigationState.all
.subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
window.__iavE2eNavigationState__ = {
orientation: Array.from(orientation),
perspectiveOrientation: Array.from(perspectiveOrientation),
perspectiveZoom,
zoom,
position: Array.from(position)
}
rs()
})
})
await getPr()
})
this.__trackingNavigationState__ = true
}
const returnVal = await this._browser.executeScript(() => window.__iavE2eNavigationState__)
return returnVal
}
}
module.exports = {
WdIavPage,
WdLayoutPage,
WdBase,
}
\ No newline at end of file
const { WdBase } = require('./base')
const {
_getIndexFromArrayOfWebElements,
_getTextFromWebElement
} = require('./util')
const { ARIA_LABELS } = require('../../../common/constants')
class WdLayoutPage extends WdBase{
constructor(){
super()
}
/**
* Snackbar
*/
async getSnackbarMessage(){
const txt = await this._driver
.findElement( By.css('simple-snack-bar') )
.findElement( By.css('span') )
.getText()
return txt
}
async clickSnackbarAction(){
await this._driver
.findElement( By.css('simple-snack-bar') )
.findElement( By.css('button') )
.click()
}
/**
* Bottomsheet
*/
_getBottomSheet() {
return this._driver.findElement( By.css('mat-bottom-sheet-container') )
}
_getBottomSheetList(){
return this._getBottomSheet().findElements( By.css('mat-list-item') )
}
async getBottomSheetList(){
const listItems = await this._getBottomSheetList()
const output = []
for (const item of listItems) {
output.push(
await _getTextFromWebElement(item)
)
}
return output
}
async clickNthItemFromBottomSheetList(index, cssSelector){
const list = await this._getBottomSheetList()
if (!list[index]) throw new Error(`index out of bound: ${index} in list with size ${list.length}`)
if (cssSelector) {
await list[index]
.findElement( By.css(cssSelector) )
.click()
} else {
await list[index].click()
}
}
/**
* Modal
*/
_getModal(){
return this._browser.findElement( By.css('mat-dialog-container') )
}
async dismissModal() {
try {
const okBtn = await this._getModal()
.findElement( By.css('mat-dialog-actions') )
.findElement( By.css('button[color="primary"]') )
await okBtn.click()
} catch (e) {
}
}
_getModalBtns(){
return this._getModal().findElements( By.tagName('button') )
}
async getModalText(){
const el = await this._getModal()
const txt = await _getTextFromWebElement(el)
return txt
}
async getModalActions(){
const btns = await this._getModalBtns()
const arr = []
for (const btn of btns){
arr.push(await _getTextFromWebElement(btn))
}
return arr
}
/**
*
* @param {string|RegExp} text
* @description search criteria for the btn to click
*/
async clickModalBtnByText(text){
const btns = await this._getModalBtns()
const arr = await this.getModalActions()
if (typeof text === 'string') {
const idx = arr.indexOf(text)
if (idx < 0) throw new Error(`clickModalBtnByText: ${text} not found.`)
await btns[idx].click()
return
}
if (text instanceof RegExp) {
const idx = arr.findIndex(item => text.test(item))
if (idx < 0) throw new Error(`clickModalBtnByText: regexp ${text.toString()} not found`)
await btns[idx].click()
return
}
throw new Error(`clickModalBtnByText arg must be instance of string or regexp`)
}
/**
* ExpansionPanel
*/
/**
*
* @param {string|RegExp} text
* @description search criteria for the expansion panel title
*/
async _getExpansionPanel(text){
const expPanels = await this._browser.findElements(
By.css(`mat-expansion-panel`)
)
const expPanelHdrs = []
for (const expPanel of expPanels) {
expPanelHdrs.push(
await expPanel.findElement(
By.css(`mat-expansion-panel-header`)
)
)
}
const idx = await _getIndexFromArrayOfWebElements(text, expPanelHdrs)
return idx >= 0 && expPanels[idx]
}
/**
*
* @param {Object} expPanel webelement of the mat expansion panel
* @returns {Promise<boolean>}
*/
async _expansionPanelIsOpen(expPanel){
const classString = await expPanel.getAttribute('class')
/**
* mat-expanded gets appended when mat-expansion panel is set to open
*/
return classString.indexOf('mat-expanded') >= 0
}
/**
*
* @param {string|RegExp} name Text of the expansion panel header
* @param {boolean} flag @optional State to set the expansion panel. Leave empty for toggle.
*/
async toggleExpansionPanelState(name, flag){
const expPanel = await this._getExpansionPanel(name)
if (!expPanel) throw new Error(`expansionPanel ${name} could not be found`)
if (typeof flag === 'undefined') {
await expPanel.findElement(
By.css(`mat-expansion-panel-header`)
).click()
return
}
const currentOpen = await this._expansionPanelIsOpen(expPanel)
if (currentOpen !== flag) {
await expPanel.findElement(
By.css(`mat-expansion-panel-header`)
).click()
}
}
/**
* Other
*/
async _findTitleCard(title) {
const titleCards = await this._browser
.findElement( By.css('ui-splashscreen') )
.findElements( By.css('mat-card') )
const idx = await _getIndexFromArrayOfWebElements(title, titleCards)
if (idx >= 0) return titleCards[idx]
else throw new Error(`${title} does not fit any titleCards`)
}
async selectTitleCard( title ) {
const titleCard = await this._findTitleCard(title)
await titleCard.click()
}
async selectTitleTemplateParcellation(templateName, parcellationName){
throw new Error(`selectTitleTemplateParcellation has been deprecated. use selectAtlasTemplateParcellation`)
}
/**
* _setAtlasSelectorExpanded
* toggle/set the open state of the atlas-layer-selector element
* If the only argument (flag) is not provided, it will toggle the atlas-layer-selector
*
* Will throw if atlas-layer-selector is not in the DOM
*
* @param {boolean} flag
*
*/
async _setAtlasSelectorExpanded(flag) {
const atlasLayerSelectorEl = this._browser.findElement(
By.css('atlas-layer-selector')
)
const openedFlag = (await atlasLayerSelectorEl.getAttribute('data-opened')) === 'true'
if (typeof flag === 'undefined' || flag !== openedFlag) {
await atlasLayerSelectorEl.findElement(By.css(`button[aria-label="${ARIA_LABELS.TOGGLE_ATLAS_LAYER_SELECTOR}"]`)).click()
}
}
async changeTemplate(templateName){
if (!templateName) throw new Error(`templateName needs to be provided`)
await this._setAtlasSelectorExpanded(true)
await this.wait(1000)
const allTiles = await this._browser
.findElement( By.css('atlas-layer-selector') )
.findElements( By.css(`mat-grid-tile`) )
const idx = await _getIndexFromArrayOfWebElements(templateName, allTiles)
if (idx >= 0) await allTiles[idx].click()
else throw new Error(`#changeTemplate: templateName ${templateName} cannot be found.`)
}
async changeParc(parcName) {
throw new Error(`changeParc NYI`)
}
async selectAtlasTemplateParcellation(atlasName, templateName, parcellationName, parcVersion) {
if (!atlasName) throw new Error(`atlasName needs to be provided`)
try {
/**
* if at title screen
*/
await (await this._findTitleCard(atlasName)).click()
} catch (e) {
/**
* if not at title screen
* select from dropdown
*/
}
if (templateName) {
await this.wait(1000)
await this.waitUntilAllChunksLoaded()
await this.changeTemplate(templateName)
}
if (parcellationName) {
await this.wait(1000)
await this.waitUntilAllChunksLoaded()
await this.changeParc(parcellationName)
}
await this._setAtlasSelectorExpanded(false)
}
/**
* Sidenav
*/
_getSideNavPrimary(){
return this._browser.findElement(
By.css('mat-drawer[data-mat-drawer-primary-open]')
)
}
async _getSideNavPrimaryExpanded(){
return (await this._getSideNavPrimary()
.getAttribute('data-mat-drawer-primary-open')) === 'true'
}
_getSideNavSecondary(){
return this._browser.findElement(
By.css('mat-drawer[data-mat-drawer-secondary-open]')
)
}
async _getSideNavSecondaryExpanded(){
return (await this._getSideNavSecondary()
.getAttribute('data-mat-drawer-secondary-open')) === 'true'
}
async _setSideNavPrimaryExpanded(flag) {
const openedFlag = await this._getSideNavPrimaryExpanded()
if (typeof flag === 'undefined' || flag !== openedFlag) {
await this._browser.findElement(By.css(`button[aria-label="${ARIA_LABELS.TOGGLE_SIDE_PANEL}"]`)).click()
}
}
async getSideNavTag(){
return await this._browser
.findElement( By.css('[mat-drawer-trigger]') )
.findElement( By.css('i') )
}
sideNavIsVisible(){
throw new Error(`sideNavIsVisible is deprecated`)
}
_getSideNavTab(){
return this._browser
.findElement( By.css('[mat-drawer-trigger]') )
.findElement( By.css('i') )
}
sideNavTabIsVisible(){
return this._getSideNavTab().isDisplayed()
}
clickSideNavTab(){
return this._getSideNavTab().click()
}
/**
* StatusPanel
*/
_getStatusPanel(){
return this._browser.findElement( By.css('[mat-drawer-status-panel]') )
}
async statusPanelIsVisible() {
try {
return await this._getStatusPanel().isDisplayed()
} catch (e) {
return false
}
}
clickStatusPanel() {
// Will throw if status panel is not visible
return this._getStatusPanel().click()
}
async getTemplateInfo(){
throw new Error(`getTemplateInfo has been deprecated. Implmenet new method of getting info`)
}
// will throw if additional layer control is not visible
additionalLayerControlIsExpanded() {
throw new Error(`additionalLayerControlIsExpanded is deprecated`)
}
/**
* TODO? deprecate
*/
async toggleNthLayerControl(idx) {
const els = await this._browser
.findElement(
By.css(`[aria-label="${ARIA_LABELS.ADDITIONAL_VOLUME_CONTROL}"]`)
)
.findElements( By.css(`[aria-label="${ARIA_LABELS.TOGGLE_SHOW_LAYER_CONTROL}"]`))
if (!els[idx]) throw new Error(`toggleNthLayerControl index out of bound: accessor ${idx} with length ${els.length}`)
await els[idx].click()
}
// Signin banner
_getToolsIcon(){
return this._driver
.findElement( By.css('[aria-label="Show tools and plugins"]') )
}
async showToolsMenu(){
await this._getToolsIcon().click()
}
_getToolsMenu(){
return this._driver
.findElement( By.css('[aria-label="Tools and plugins menu"]') )
}
_getAllTools(){
return this._getToolsMenu().findElements( By.css('[role="menuitem"]') )
}
async getVisibleTools(){
// may throw if tools menu not visible
const menuItems = await this._getAllTools()
const returnArr = []
for (const menuItem of menuItems){
returnArr.push(
await _getTextFromWebElement(menuItem)
)
}
return returnArr
}
async clickOnNthTool(index, cssSelector){
const menuItems = await this._getAllTools()
if (!menuItems[index]) throw new Error(`index out of bound: accessing index ${index} of length ${menuItems.length}`)
if (cssSelector) await menuItems[index].findElement( By.css(cssSelector) ).click()
else await menuItems[index].click()
}
_getFavDatasetIcon(){
return this._driver
.findElement( By.css('[aria-label="Show pinned datasets"]') )
}
async getNumberOfFavDataset(){
const attr = await this._getFavDatasetIcon().getAttribute('pinned-datasets-length')
return Number(attr)
}
async showPinnedDatasetPanel(){
await this._getFavDatasetIcon().click()
await this.wait(500)
}
_getPinnedDatasetPanel(){
return this._driver
.findElement(
By.css('[aria-label="Pinned datasets panel"]')
)
}
async getPinnedDatasetsFromOpenedPanel(){
const list = await this._getPinnedDatasetPanel()
.findElements(
By.tagName('mat-list-item')
)
const returnArr = []
for (const el of list) {
const text = await _getTextFromWebElement(el)
returnArr.push(text)
}
return returnArr
}
async unpinNthDatasetFromOpenedPanel(index){
const list = await this._getPinnedDatasetPanel()
.findElements(
By.tagName('mat-list-item')
)
if (!list[index]) throw new Error(`index out of bound: ${index} in list with size ${list.length}`)
await list[index]
.findElement( By.css('[aria-label="Toggle pinning this dataset"]') )
.click()
}
_getWidgetPanel(title){
return this._driver.findElement( By.css(`[aria-label="Widget for ${title}"]`) )
}
async widgetPanelIsDispalyed(title){
try {
const isDisplayed = await this._getWidgetPanel(title).isDisplayed()
return isDisplayed
} catch (e) {
console.warn(`widgetPanelIsDisplayed error`, e)
return false
}
}
async closeWidgetByname(title){
await this._getWidgetPanel(title)
.findElement( By.css(`[aria-label="close"]`) )
.click()
}
}
module.exports = {
WdLayoutPage,
WdBase,
}
\ No newline at end of file
function _getTextFromWebElement(webElement) {
return webElement.getText()
}
async function _getIndexFromArrayOfWebElements(search, webElements) {
const texts = await Promise.all(
webElements.map(_getTextFromWebElement)
)
return texts.findIndex(text => search instanceof RegExp
? search.test(text)
: text.indexOf(search) >= 0)
}
module.exports = {
_getTextFromWebElement,
_getIndexFromArrayOfWebElements
}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment