<commit_before>package org.broadinstitute.dsde.firecloud.page.workspaces.methodconfigs

import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

import com.typesafe.scalalogging.LazyLogging
import org.broadinstitute.dsde.firecloud.{FireCloudConfig, FireCloudView}
import org.broadinstitute.dsde.firecloud.component._
import org.broadinstitute.dsde.firecloud.component.Component._
import org.broadinstitute.dsde.firecloud.fixture.DownloadUtil
import org.broadinstitute.dsde.firecloud.page.workspaces.WorkspacePage
import org.broadinstitute.dsde.firecloud.page.workspaces.monitor.SubmissionDetailsPage
import org.broadinstitute.dsde.firecloud.page.PageUtil
import org.broadinstitute.dsde.workbench.service.util.Util
import org.openqa.selenium.{TimeoutException, WebDriver}
import org.scalatest.concurrent.Eventually
import org.scalatest.concurrent.Eventually.eventually
import org.scalatest.selenium.Page

import scala.util.{Failure, Success, Try}

class WorkspaceMethodConfigDetailsPage(namespace: String, name: String, methodConfigNamespace: String, val methodConfigName: String)(implicit webDriver: WebDriver)
  extends WorkspacePage(namespace, name) with Page with PageUtil[WorkspaceMethodConfigDetailsPage] with LazyLogging with Eventually with DownloadUtil  {

  override def awaitReady(): Unit = {
    await condition isLoaded
    await spinner "Checking permissions..."
  }

  override val url: String = s"${FireCloudConfig.FireCloud.baseUrl}#workspaces/$namespace/$name/method-configs/$methodConfigNamespace/$methodConfigName"

  private val openLaunchAnalysisModalButton = Button("open-launch-analysis-modal-button")
  private val openEditModeButton = Button("edit-method-config-button")
  private val editMethodConfigNameInput = TextField("edit-method-config-name-input")
  private val saveEditedMethodConfigButton = Button("save-edited-method-config-button")
  private val editMethodConfigSnapshotIdSelect = Select("edit-method-config-snapshot-id-select")
  private val editMethodConfigRootEntityTypeSelect = Select("edit-method-config-root-entity-type-select")
  private val deleteMethodConfigButton = Button("delete-method-config-button")
  private val modalConfirmDeleteButton = Button("modal-confirm-delete-button")
  private val snapshotRedactedLabel = Label("snapshot-redacted-title")
  private val dataModelCheckbox = Checkbox("data-model-checkbox")
  private val populateWithJsonLink = Link("populate-with-json-link")
  private val downloadInputsJsonLink = Link("download-link")
  val inputsTable = Table("inputs-table")


  def clickLaunchAnalysis[T <: FireCloudView](page: T): T = {
    openLaunchAnalysisModalButton.doClick()
    Try(
      page.awaitReady() // check if click happened
    ) match {
      case Failure(e) => // click failed
        openLaunchAnalysisModalButton.doClick()
        await ready page
      case Success(some) => // clicked
        page
    }
  }

  def launchAnalysis(rootEntityType: String, entityId: String, expression: String = "", enableCallCaching: Boolean = true): SubmissionDetailsPage = {
    val launchModal = openLaunchAnalysisModal()
    launchModal.launchAnalysis(rootEntityType, entityId, expression, enableCallCaching)
    try {
      launchModal.awaitDismissed()
      await ready new SubmissionDetailsPage(namespace, name)
    } catch {
      case e: TimeoutException =>
        if (webDriver.getPageSource.contains("Please select an entity")) {
          logger.warn(s"Retrying select participant $entityId and click Launch button in Launch Analysis modal")
          launchModal.searchAndSelectEntity(entityId)
          launchModal.clickLaunchButton()
          await ready new SubmissionDetailsPage(namespace, name)
        } else {
          throw e
        }
    }
  }

  def openLaunchAnalysisModal(): LaunchAnalysisModal = {
    clickLaunchAnalysis(new LaunchAnalysisModal())
  }

  def clickLaunchAnalysisButtonError(): MessageModal = {
    clickLaunchAnalysis(new MessageModal())
  }

  def isEditing: Boolean = {
    if (openEditModeButton.isVisible) false
    else if (saveEditedMethodConfigButton.isVisible) true
    else throw new Exception("Could not determine edit mode of method config view")
  }

  def openEditMode(expectSuccess: Boolean = true): Unit = {
    openEditModeButton.doClick()
    if (expectSuccess)
      saveEditedMethodConfigButton.awaitVisible()
  }

  def checkSaveButtonState(): String = {
    saveEditedMethodConfigButton.getState
  }

  def saveEdits(expectSuccess: Boolean = true): Unit = {
    // The button can sometimes scroll off the page and become unclickable. Therefore we need to scroll it into view.
    saveEditedMethodConfigButton.scrollToVisible()
    saveEditedMethodConfigButton.doClick()
    if (expectSuccess)
      openLaunchAnalysisModalButton.awaitVisible()
  }

  def editMethodConfig(newName: Option[String] = None, newSnapshotId: Option[Int] = None, newRootEntityType: Option[String] = None,
                       inputs: Option[Map[String, String]] = None, outputs: Option[Map[String, String]] = None): Unit = {
    openEditMode()
    await spinner "Loading attributes..."

    if (newName.isDefined) { editMethodConfigNameInput.setText(newName.get) }
    if (newSnapshotId.isDefined) { changeSnapshotId(newSnapshotId.get) }
    if (newRootEntityType.isDefined) { editMethodConfigRootEntityTypeSelect.select(newRootEntityType.get)}
    if (inputs.isDefined) { changeInputsOutputs(inputs.get) }
    if (outputs.isDefined) { changeInputsOutputs(outputs.get)}

    saveEdits()
  }

  def toggleDataModel(): Boolean = {
    if (dataModelCheckbox.isChecked) dataModelCheckbox.ensureUnchecked()
    else dataModelCheckbox.ensureChecked()
    dataModelCheckbox.isChecked
  }

  def readRootEntityTypeSelect: String = {
    editMethodConfigRootEntityTypeSelect.value
  }

  def selectRootEntityType(rootEntityType: String): Unit = {
    editMethodConfigRootEntityTypeSelect.select(rootEntityType)
  }

  def clickAndReadSuggestions(field: String): Seq[String] = {
    val dataTestId = s"$field-text-input"
    click on testId(dataTestId)
    // wait for dropdown to become expanded
    await condition (find(testId(dataTestId)).exists(_.underlying.getAttribute("aria-expanded") == "true"), 10)
    // wait for dropdown to contain at least one WebElement
    val xpathSelector = s"//*[@data-test-id='$field-suggestions']/*/li"
    await condition (findAll(xpath(xpathSelector)).map(elem => elem.isDisplayed).nonEmpty, 10)
    findAll(xpath(xpathSelector)).map(_.text).toSeq
  }

  def readFieldValue(field: String): String = {
    if (isEditing) searchField(testId(s"$field-text-input")).value
    else find(testId(s"$field-display")).map(_.underlying.getText).getOrElse("")
  }

  def populateInputsFromJson(file: File): Unit = {
    populateWithJsonLink.doClick()
    val modal = await ready new PopulateFromJsonModal
    modal.importFile(file.getAbsolutePath)
  }

  def downloadInputsJson(downloadPath: String, fileName: String): String = {
    downloadFile(downloadPath, fileName, downloadInputsJsonLink)
  }<commit_msg>fix stable element exception in Suggestion dropdown (#1375)<commit_after>package org.broadinstitute.dsde.firecloud.page.workspaces.methodconfigs

import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

import com.typesafe.scalalogging.LazyLogging
import org.broadinstitute.dsde.firecloud.{FireCloudConfig, FireCloudView}
import org.broadinstitute.dsde.firecloud.component._
import org.broadinstitute.dsde.firecloud.component.Component._
import org.broadinstitute.dsde.firecloud.fixture.DownloadUtil
import org.broadinstitute.dsde.firecloud.page.workspaces.WorkspacePage
import org.broadinstitute.dsde.firecloud.page.workspaces.monitor.SubmissionDetailsPage
import org.broadinstitute.dsde.firecloud.page.PageUtil
import org.broadinstitute.dsde.workbench.service.util.Util
import org.openqa.selenium.{TimeoutException, WebDriver}
import org.scalatest.concurrent.Eventually
import org.scalatest.concurrent.Eventually.eventually
import org.scalatest.selenium.Page

import scala.util.{Failure, Success, Try}

class WorkspaceMethodConfigDetailsPage(namespace: String, name: String, methodConfigNamespace: String, val methodConfigName: String)(implicit webDriver: WebDriver)
  extends WorkspacePage(namespace, name) with Page with PageUtil[WorkspaceMethodConfigDetailsPage] with LazyLogging with Eventually with DownloadUtil  {

  override def awaitReady(): Unit = {
    await condition isLoaded
    await spinner "Checking permissions..."
  }

  override val url: String = s"${FireCloudConfig.FireCloud.baseUrl}#workspaces/$namespace/$name/method-configs/$methodConfigNamespace/$methodConfigName"

  private val openLaunchAnalysisModalButton = Button("open-launch-analysis-modal-button")
  private val openEditModeButton = Button("edit-method-config-button")
  private val editMethodConfigNameInput = TextField("edit-method-config-name-input")
  private val saveEditedMethodConfigButton = Button("save-edited-method-config-button")
  private val editMethodConfigSnapshotIdSelect = Select("edit-method-config-snapshot-id-select")
  private val editMethodConfigRootEntityTypeSelect = Select("edit-method-config-root-entity-type-select")
  private val deleteMethodConfigButton = Button("delete-method-config-button")
  private val modalConfirmDeleteButton = Button("modal-confirm-delete-button")
  private val snapshotRedactedLabel = Label("snapshot-redacted-title")
  private val dataModelCheckbox = Checkbox("data-model-checkbox")
  private val populateWithJsonLink = Link("populate-with-json-link")
  private val downloadInputsJsonLink = Link("download-link")
  val inputsTable = Table("inputs-table")


  def clickLaunchAnalysis[T <: FireCloudView](page: T): T = {
    openLaunchAnalysisModalButton.doClick()
    Try(
      page.awaitReady() // check if click happened
    ) match {
      case Failure(e) => // click failed
        openLaunchAnalysisModalButton.doClick()
        await ready page
      case Success(some) => // clicked
        page
    }
  }

  def launchAnalysis(rootEntityType: String, entityId: String, expression: String = "", enableCallCaching: Boolean = true): SubmissionDetailsPage = {
    val launchModal = openLaunchAnalysisModal()
    launchModal.launchAnalysis(rootEntityType, entityId, expression, enableCallCaching)
    try {
      launchModal.awaitDismissed()
      await ready new SubmissionDetailsPage(namespace, name)
    } catch {
      case e: TimeoutException =>
        if (webDriver.getPageSource.contains("Please select an entity")) {
          logger.warn(s"Retrying select participant $entityId and click Launch button in Launch Analysis modal")
          launchModal.searchAndSelectEntity(entityId)
          launchModal.clickLaunchButton()
          await ready new SubmissionDetailsPage(namespace, name)
        } else {
          throw e
        }
    }
  }

  def openLaunchAnalysisModal(): LaunchAnalysisModal = {
    clickLaunchAnalysis(new LaunchAnalysisModal())
  }

  def clickLaunchAnalysisButtonError(): MessageModal = {
    clickLaunchAnalysis(new MessageModal())
  }

  def isEditing: Boolean = {
    if (openEditModeButton.isVisible) false
    else if (saveEditedMethodConfigButton.isVisible) true
    else throw new Exception("Could not determine edit mode of method config view")
  }

  def openEditMode(expectSuccess: Boolean = true): Unit = {
    openEditModeButton.doClick()
    if (expectSuccess)
      saveEditedMethodConfigButton.awaitVisible()
  }

  def checkSaveButtonState(): String = {
    saveEditedMethodConfigButton.getState
  }

  def saveEdits(expectSuccess: Boolean = true): Unit = {
    // The button can sometimes scroll off the page and become unclickable. Therefore we need to scroll it into view.
    saveEditedMethodConfigButton.scrollToVisible()
    saveEditedMethodConfigButton.doClick()
    if (expectSuccess)
      openLaunchAnalysisModalButton.awaitVisible()
  }

  def editMethodConfig(newName: Option[String] = None, newSnapshotId: Option[Int] = None, newRootEntityType: Option[String] = None,
                       inputs: Option[Map[String, String]] = None, outputs: Option[Map[String, String]] = None): Unit = {
    openEditMode()
    await spinner "Loading attributes..."

    if (newName.isDefined) { editMethodConfigNameInput.setText(newName.get) }
    if (newSnapshotId.isDefined) { changeSnapshotId(newSnapshotId.get) }
    if (newRootEntityType.isDefined) { editMethodConfigRootEntityTypeSelect.select(newRootEntityType.get)}
    if (inputs.isDefined) { changeInputsOutputs(inputs.get) }
    if (outputs.isDefined) { changeInputsOutputs(outputs.get)}

    saveEdits()
  }

  def toggleDataModel(): Boolean = {
    if (dataModelCheckbox.isChecked) dataModelCheckbox.ensureUnchecked()
    else dataModelCheckbox.ensureChecked()
    dataModelCheckbox.isChecked
  }

  def readRootEntityTypeSelect: String = {
    editMethodConfigRootEntityTypeSelect.value
  }

  def selectRootEntityType(rootEntityType: String): Unit = {
    editMethodConfigRootEntityTypeSelect.select(rootEntityType)
  }

  def clickAndReadSuggestions(field: String): Seq[String] = {
    val dataTestId = s"$field-text-input"
    val listOptionXpath = s"//div[@data-test-id='$field-suggestions']/ul[@role='listbox']/li[@role='option']"

    val suggestionTextfield: Element = find(testId(dataTestId)).get
    val expandedDropdownId = suggestionTextfield.underlying.getAttribute("aria-owns")

    click on suggestionTextfield // click in textfield to invoke list dropdown
    await condition find(testId(dataTestId)).exists(_.underlying.getAttribute("aria-expanded") == "true")

    // wait for dropdown to contain at least one item
    await condition {
      find(xpath(s"//div[@id='$expandedDropdownId']")).exists(_.isDisplayed)
      findAll(xpath(listOptionXpath)).map(_.text).toSeq.nonEmpty // getting Element's text force screen scroll if item is outside of viewport
    }

    findAll(xpath(listOptionXpath)).map(_.text).toSeq
  }

  def readFieldValue(field: String): String = {
    if (isEditing) searchField(testId(s"$field-text-input")).value
    else find(testId(s"$field-display")).map(_.underlying.getText).getOrElse("")
  }

  def populateInputsFromJson(file: File): Unit = {
    populateWithJsonLink.doClick()
    val modal = await ready new PopulateFromJsonModal
    modal.importFile(file.getAbsolutePath)
  }

  def downloadInputsJson(downloadPath: String, fileName: String): String = {
    downloadFile(downloadPath, fileName, downloadInputsJsonLink)
  }
{'fraction_non_alphanumeric': 0.0983167296461697, 'fraction_numerical': 0.0005496392992098935, 'mean_word_length': 4.773899246330821, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6749940', 'n_tokens_mistral': 4151, 'n_tokens_neox': 4092, 'n_words': 853}