diff --git a/.github/workflows/ci-container-build.yaml b/.github/workflows/ci-container-build.yaml index bb555ec5d5..82d7552b11 100644 --- a/.github/workflows/ci-container-build.yaml +++ b/.github/workflows/ci-container-build.yaml @@ -17,11 +17,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Container Run run: | ./run-platform.sh -b @@ -48,4 +50,51 @@ jobs: docker compose -f docker/docker-compose.yaml down -v exit 1 fi - docker compose -f docker/docker-compose.yaml down -v + curl -vvv http://frontend.unstract.localhost + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.9' + + - uses: browser-actions/setup-chrome@v1 + with: + chrome-version: latest + install-chromedriver: false + + - name: Cache tox environments + uses: actions/cache@v4 + with: + path: .tox/ + key: ${{ runner.os }}-tox-${{ hashFiles('**/pyproject.toml', '**/tox.ini') }} + restore-keys: | + ${{ runner.os }}-tox- + + - name: Install tox + run: pip install tox + + - name: Run tox + id: tox + run: | + tox -e e2e && + printf '$$\\textcolor{#0051db}{\\tt{E2E\\ Tests}}$$\n\n%s' "$(cat e2e-report.md)" > e2e-report.md + + - name: Render the report to the PR + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: e2e-test-report + recreate: true + path: e2e-report.md + + - name: Output reports to the job summary when tests fail + shell: bash + run: | + if [ -f "e2e-report.md" ]; then + echo "
E2E Test Report" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + cat "e2e-report.md" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + fi + - name: Stop any running containers + run: docker compose -f docker/docker-compose.yaml down -v diff --git a/tests/e2e/requirements.txt b/tests/e2e/requirements.txt new file mode 100644 index 0000000000..150a6c4ec4 --- /dev/null +++ b/tests/e2e/requirements.txt @@ -0,0 +1,6 @@ +pytest~=8.0 +pytest-mock~=3.0 +pytest-cov~=6.0 +pytest-md-report~=0.6.0 +selenium~=4.28 +webdriver-manager~=4.0 diff --git a/tests/e2e/test_login.py b/tests/e2e/test_login.py new file mode 100644 index 0000000000..92ff2f811a --- /dev/null +++ b/tests/e2e/test_login.py @@ -0,0 +1,78 @@ +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service as ChromeService +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait +from webdriver_manager.chrome import ChromeDriverManager + + +class TestLogin: + def setup_method(self, method): + options = Options() + options.add_argument("--headless=new") + self.driver = webdriver.Chrome( + service=ChromeService(ChromeDriverManager().install()), options=options + ) + self.driver.implicitly_wait(5) + + def teardown_method(self, method): + self.driver.quit() + + def _check_page_load(self): + try: + self.driver.get("http://frontend.unstract.localhost") + except Exception as e: + print(f"Page load exception: {e}") + return False + else: + return True + + def test_login(self): + # Wait for the page to load + WebDriverWait(self.driver, timeout=30).until( + lambda _: self._check_page_load(), + "Page load failed", + ) + WebDriverWait(self.driver, timeout=30).until( + EC.presence_of_element_located((By.CLASS_NAME, "login-main")) + ) + print(self.driver.find_element(By.CLASS_NAME, "login-main").text) + # Set the window size + self.driver.set_window_size(960, 615) + + # Explicit wait for the button to be clickable + WebDriverWait(self.driver, timeout=10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "span")), + "Button not clickable.", + ).click() + + # Explicit wait for the username field to be visible + username_field = WebDriverWait(self.driver, timeout=10).until( + EC.visibility_of_element_located((By.ID, "username")), + "Username field not visible.", + ) + username_field.click() + username_field.send_keys("unstract") + + # Explicit wait for the password field to be visible + password_field = WebDriverWait(self.driver, timeout=10).until( + EC.visibility_of_element_located((By.ID, "password")), + "Password field not visible.", + ) + password_field.send_keys("unstract") + + # Explicit wait for the login button to be clickable + WebDriverWait(self.driver, timeout=10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "input:nth-child(11)")), + "Login button not clickable.", + ).click() + + # Wait for the URL to change to indicate successful login + WebDriverWait(self.driver, timeout=10).until( + lambda _: self.driver.current_url.endswith("/mock_org/onboard"), + "Login failed or URL did not change.", + ) + + # Close the browser + self.driver.close() diff --git a/tox.ini b/tox.ini index 196cf06021..52cb7f7db9 100644 --- a/tox.ini +++ b/tox.ini @@ -17,3 +17,9 @@ commands_pre = sh -c '[ -f cloud_requirements.txt ] && pip install -r cloud_requirements.txt || echo "cloud_requirements.txt not found"' commands = pytest -v --md-report-verbose=1 --md-report --md-report-flavor gfm --md-report-output ../runner-report.md + +[testenv:e2e] +commands_pre = + pip install -r tests/e2e/requirements.txt +commands = + pytest -s -v --md-report-verbose=1 --md-report --md-report-flavor gfm --md-report-output e2e-report.md tests/e2e/