import { BrowserTabsUtil } from '../../utils/BrowserTabsUtil' ; import { CLASSES } from '../../inversify.types' ; import { DebugView } from '../../pageobjects/ide/DebugView' ; import { DialogWindow } from '../../pageobjects/ide/DialogWindow' ; import { DriverHelper } from '../../utils/DriverHelper' ; import { e2eContainer } from '../../inversify.config' ; import { Editor } from '../../pageobjects/ide/Editor' ; import { Ide , LeftToolbarButton } from '../../pageobjects/ide/Ide' ; import { Key , error , By } from 'selenium-webdriver' ; import { Logger } from '../../utils/Logger' ; import { ProjectTree } from '../../pageobjects/ide/ProjectTree' ; import { Terminal } from '../../pageobjects/ide/Terminal' ; import { TestConstants } from '../../TestConstants' ; import { TimeoutConstants } from '../../TimeoutConstants' ; import { TopMenu } from '../../pageobjects/ide/TopMenu' ; import * as fs from 'fs' ; import axios from 'axios' ; import CheReporter from '../../driver/CheReporter' ; const ide = e2eContainer . get ( CLASSES . Ide ) ; const projectTree = e2eContainer . get ( CLASSES . ProjectTree ) ; const browserTabsUtil = e2eContainer . get ( CLASSES . BrowserTabsUtil ) ; const projectName = 'java-spring-petclinic' ; const workspaceRootFolderName = 'src' ; const pathToJavaFolder = `${ projectName }/${ workspaceRootFolderName }/main/java/org/springframework/samples/petclinic` ; const javaFileName = 'PetClinicApplication.java' ; const editor = e2eContainer . get ( CLASSES . Editor ) ; const classPathFilename = '.classpath' ; const driverHelper = e2eContainer . get ( CLASSES . DriverHelper ) ; const codeNavigationClassName = 'SpringApplication.class' ; const topMenu = e2eContainer . get ( CLASSES . TopMenu ) ; const globalTaskScope = 'Global' ; const terminal = e2eContainer . get ( CLASSES . Terminal ) ; const warningDialog = e2eContainer . get ( CLASSES . DialogWindow ) ; const debugView = e2eContainer . get ( CLASSES . DebugView ) ; const welcomeControllerJavaFileName = 'WelcomeController.java' ; const SpringAppLocators = { springTitleLocator : By . xpath ( '//div[@class=\'container-fluid\']//h2[text()=\'Welcome\']' ) , springMenuButtonLocator : By . css ( 'button[data-target=\'#main-navbar\']' ) , springErrorButtonLocator : By . xpath ( '//div[@id=\'main-navbar\']//span[text()=\'Error\']' ) , springHomeButtonLocator : By . className ( 'navbar-brand' ) , springErrorMessageLocator : By . xpath ( `//h2[text()='Something happened...']` ) } ; suite ( 'Workspace creation via factory url' , async ( ) => { let factoryUrl = `${ TestConstants . TS_SELENIUM_DEVWORKSPACE_URL }` ; const workspaceRootFolderName = 'src' ; suite ( 'Open factory URL' , async ( ) => { test ( `Navigating to factory URL` , async ( ) => { await browserTabsUtil . navigateTo ( factoryUrl ) ; } ) ; test ( 'Register running workspace' , async ( ) => { CheReporter . registerRunningWorkspace ( projectName ) ; } ) ; test ( 'Register running workspace' , async ( ) => { await ide . waitIde ( TimeoutConstants . TS_SELENIUM_START_WORKSPACE_TIMEOUT ) ; await projectTree . openProjectTreeContainer ( ) ; await projectTree . waitProjectImported ( projectName , workspaceRootFolderName ) ; } ) ; } ) ; } ) ; suite ( 'Language server validation' , async ( ) => { test ( 'Java LS initialization' , async ( ) => { await projectTree . expandPathAndOpenFile ( pathToJavaFolder , javaFileName ) ; await editor . selectTab ( javaFileName ) ; try { await ide . checkLsInitializationStart ( 'Activating Language Support for Java' ) ; await ide . waitStatusBarTextAbsence ( 'Activating Language Support for Java' , 900000 ) ; } catch ( err ) { if ( ! ( err instanceof error . TimeoutError ) ) { throw err ; } console . log ( 'Known flakiness has occurred https://github.com/eclipse/che/issues/17864' ) ; await ide . waitStatusBarContains ( 'Activating Java Test Runner' ) ; await ide . waitStatusBarTextAbsence ( 'Activating Java Test Runner' , 900000 ) ; } await checkJavaPathCompletion ( ) ; } ) ; test ( 'Autocomplete' , async ( ) => { await editor . moveCursorToLineAndChar ( javaFileName , 32 , 17 ) ; await editor . pressControlSpaceCombination ( javaFileName ) ; await editor . waitSuggestionContainer ( ) ; await editor . waitSuggestion ( javaFileName , 'SpringApplication - org.springframework.boot' ) ; } ) ; test ( 'Error highlighting' , async ( ) => { await driverHelper . getDriver ( ) . sleep ( TimeoutConstants . TS_SUGGESTION_TIMEOUT ) ; const textForErrorDisplaying = '$' ; await editor . type ( javaFileName , textForErrorDisplaying , 30 ) ; try { await editor . waitErrorInLine ( 30 , javaFileName , TimeoutConstants . TS_ERROR_HIGHLIGHTING_TIMEOUT ) ; } catch ( err ) { Logger . debug ( 'Workaround for the https://github.com/eclipse/che/issues/18974.' ) ; await browserTabsUtil . refreshPage ( ) ; await ide . waitAndSwitchToIdeFrame ( ) ; await ide . waitIde ( ) ; await editor . waitErrorInLine ( 30 , javaFileName , TimeoutConstants . TS_ERROR_HIGHLIGHTING_TIMEOUT * 2 ) ; } await editor . performKeyCombination ( javaFileName , Key . chord ( Key . BACK_SPACE ) ) ; await editor . waitErrorInLineDisappearance ( 30 , javaFileName ) ; } ) ; test ( 'Suggestion' , async ( ) => { await editor . moveCursorToLineAndChar ( javaFileName , 32 , 21 ) ; await editor . pressControlSpaceCombination ( javaFileName ) ; await editor . waitSuggestionWithScrolling ( javaFileName , 'run(Class<?> primarySource, String... args) : ConfigurableApplicationContext' , 120000 ) ; } ) ; test ( 'Codenavigation' , async ( ) => { await editor . moveCursorToLineAndChar ( javaFileName , 32 , 17 ) ; await editor . performKeyCombination ( javaFileName , Key . chord ( Key . CONTROL , Key . F12 ) ) ; await editor . waitEditorAvailable ( codeNavigationClassName , TimeoutConstants . TS_EDITOR_TAB_INTERACTION_TIMEOUT * 4 ) ; } ) ; } ) ; suite ( 'Validation of workspace build and run' , async ( ) => { test ( 'Build application' , async ( ) => { const taskName = 'build' ; await topMenu . runTask ( `${ taskName }, ${ globalTaskScope }` ) ; await terminal . waitIconSuccess ( taskName , 500000 ) ; } ) ; test ( 'Run application' , async ( ) => { const taskName = 'run-with-hsqldb' ; await topMenu . runTask ( `${ taskName }, ${ globalTaskScope }` ) ; await ide . waitNotification ( 'Process 8080-tcp is now listening on port 8080. Open it ?' , 120000 ) ; await ide . clickOnNotificationButton ( 'Process 8080-tcp is now listening on port 8080. Open it ?' , 'Open In New Tab' ) ; } ) ; test ( 'Check the running application' , async ( ) => { await switchAppWindowAndCheck ( SpringAppLocators . springTitleLocator ) ; } ) ; test ( 'Close the terminal running tasks' , async ( ) => { await terminal . rejectTerminalProcess ( 'run-with-hsqldb' ) ; await terminal . closeTerminalTab ( 'run-with-hsqldb' ) ; await warningDialog . waitAndCloseIfAppear ( ) ; await terminal . closeTerminalTab ( 'build' ) ; } ) ; } ) ; suite ( 'Validation of debug functionality' , async ( ) => { let urlToPetClinicApp = '' ; test ( 'Launch debug' , async ( ) => { const taskName = 'run-debug' ; await topMenu . runTask ( `${ taskName }, ${ globalTaskScope }` ) ; await ide . waitNotification ( 'Process 8080-tcp is now listening on port 8080. Open it ?' , 180000 ) ; await ide . clickOnNotificationButton ( 'Process 8080-tcp is now listening on port 8080. Open it ?' , 'Open In New Tab' ) ; } ) ; test ( 'Check content of the launched application' , async ( ) => { const mainWindowHandle = await browserTabsUtil . getCurrentWindowHandle ( ) ; await browserTabsUtil . waitAndSwitchToAnotherWindow ( mainWindowHandle , TimeoutConstants . TS_EDITOR_TAB_INTERACTION_TIMEOUT ) ; urlToPetClinicApp = await browserTabsUtil . getCurrentUrl ( ) ; await browserTabsUtil . switchToWindow ( mainWindowHandle ) ; } ) ; test ( 'Open debug view' , async ( ) => { await projectTree . expandPathAndOpenFile ( pathToJavaFolder + '/system' , welcomeControllerJavaFileName ) ; await editor . selectTab ( welcomeControllerJavaFileName ) ; await topMenu . selectOption ( 'View' , 'Debug' ) ; await ide . waitLeftToolbarButton ( LeftToolbarButton . Debug ) ; } ) ; test ( 'Choose debug configuration' , async ( ) => { await debugView . clickOnDebugConfigurationDropDown ( ) ; await debugView . clickOnDebugConfigurationItem ( 'Debug (Attach) - Remote (java-spring-petclinic)' ) ; } ) ; test ( 'Run debug' , async ( ) => { await debugView . clickOnRunDebugButton ( ) ; await waitDebugToConnect ( ) ; } ) ; test ( 'Activate breakpoint' , async ( ) => { await editor . selectTab ( welcomeControllerJavaFileName ) ; await editor . activateBreakpoint ( welcomeControllerJavaFileName , 27 ) ; } ) ; test ( 'Check debugger stop at the breakpoint' , async ( ) => { await sendRequestToDebugApp ( urlToPetClinicApp ) ; await waitStoppedBreakpoint ( 27 ) ; } ) ; } ) ; async function checkJavaPathCompletion ( ) { if ( await ide . isNotificationPresent ( 'Classpath is incomplete. Only syntax errors will be reported' ) ) { const classpathText = fs . readFileSync ( './files/happy-path/petclinic-classpath.txt' , 'utf8' ) ; const workaroundReportText = '\n############################## \n\n' + 'Known issue: https://github.com/eclipse/che/issues/13427 \n' + '\"Java LS \"Classpath is incomplete\" warning when loading petclinic\" \n' + '\".classpath\" will be configured with next settings: \n\n' + classpathText + '\n' + '############################## \n' ; console . log ( workaroundReportText ) ; await projectTree . expandPathAndOpenFile ( projectName , classPathFilename ) ; await editor . waitEditorAvailable ( classPathFilename ) ; await editor . type ( classPathFilename , Key . chord ( Key . CONTROL , 'a' ) , 1 ) ; await editor . performKeyCombination ( classPathFilename , Key . DELETE ) ; await editor . type ( classPathFilename , classpathText , 1 ) ; await editor . waitTabWithSavedStatus ( classPathFilename ) ; } } async function switchAppWindowAndCheck ( contentLocator ) { const mainWindowHandle = await browserTabsUtil . getCurrentWindowHandle ( ) ; await browserTabsUtil . waitAndSwitchToAnotherWindow ( mainWindowHandle , TimeoutConstants . TS_EDITOR_TAB_INTERACTION_TIMEOUT ) ; for ( let index = 0 ; index < 3 ; index ++ ) { let isApplicationTitleVisible = await driverHelper . isVisible ( contentLocator ) ; if ( isApplicationTitleVisible ) { console . log ( '----------- The Spring app is available.' ) ; break ; } else { console . log ( `----------- The Spring app is not available. Attempt # ${ index }. Waiting ${ TimeoutConstants . TS_SELENIUM_DIALOG_WIDGET_TIMEOUT }s` ) ; await driverHelper . getDriver ( ) . sleep ( TimeoutConstants . TS_SELENIUM_DIALOG_WIDGET_TIMEOUT ) ; await driverHelper . getDriver ( ) . navigate ( ) . refresh ( ) ; } } await driverHelper . waitVisibility ( contentLocator ) ; await driverHelper . getDriver ( ) . close ( ) ; await browserTabsUtil . switchToWindow ( mainWindowHandle ) ; } async function waitStoppedBreakpoint ( lineNumber ) { try { await editor . waitStoppedDebugBreakpoint ( welcomeControllerJavaFileName , lineNumber ) ; } catch ( err ) { const mainWindowHandle = await browserTabsUtil . getCurrentWindowHandle ( ) ; await browserTabsUtil . waitAndSwitchToAnotherWindow ( mainWindowHandle , TimeoutConstants . TS_EDITOR_TAB_INTERACTION_TIMEOUT ) ; await driverHelper . getDriver ( ) . navigate ( ) . refresh ( ) ; await browserTabsUtil . switchToWindow ( mainWindowHandle ) ; await editor . waitStoppedDebugBreakpoint ( welcomeControllerJavaFileName , lineNumber ) ; } } async function sendRequestToDebugApp ( urlToApp ) { const httpClient = axios . create ( ) ; httpClient . defaults . timeout = 1000 ; try { await httpClient . get ( urlToApp ) ; } catch ( error ) { if ( error . message === 'timeout of 1000ms exceeded' ) { console . log ( '>>>>The debugger is set >>>>>>>>>>>>>>>>>>> ' + error . message ) ; } } } async function waitDebugToConnect ( ) { try { await debugView . waitForDebuggerToConnect ( ) ; } catch ( err ) { Logger . debug ( 'Workaround for the https://github.com/eclipse/che/issues/18034 issue.' ) ; await debugView . clickOnThreadsViewTitle ( ) ; await debugView . waitForDebuggerToConnect ( ) ; } }