Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
893 changes: 893 additions & 0 deletions .github/workflows/deploy-v2.yml

Large diffs are not rendered by default.

195 changes: 195 additions & 0 deletions .github/workflows/test-automation-v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
name: Test Automation Content Processing-v2

on:
workflow_call:
inputs:
CP_WEB_URL:
required: true
type: string
description: "Web URL for Content Processing"
TEST_SUITE:
required: false
type: string
default: "GoldenPath-Testing"
description: "Test suite to run: 'Smoke-Testing', 'GoldenPath-Testing' "
secrets:
EMAILNOTIFICATION_LOGICAPP_URL_TA:
required: false
description: "Logic App URL for email notifications"
outputs:
TEST_SUCCESS:
description: "Whether tests passed"
value: ${{ jobs.test.outputs.TEST_SUCCESS }}
TEST_REPORT_URL:
description: "URL to test report artifact"
value: ${{ jobs.test.outputs.TEST_REPORT_URL }}

env:
url: ${{ inputs.CP_WEB_URL }}
accelerator_name: "Content Processing"
test_suite: ${{ inputs.TEST_SUITE }}

jobs:
test:
runs-on: ubuntu-latest
outputs:
TEST_SUCCESS: ${{ steps.test1.outcome == 'success' || steps.test2.outcome == 'success' || steps.test3.outcome == 'success' }}
TEST_REPORT_URL: ${{ steps.upload_report.outputs.artifact-url }}
steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'

- name: Login to Azure
run: |
az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}
az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r tests/e2e-test/requirements.txt

- name: Ensure browsers are installed
run: python -m playwright install --with-deps chromium

- name: Validate URL
run: |
if [ -z "${{ env.url }}" ]; then
echo "ERROR: No URL provided for testing"
exit 1
fi
echo "Testing URL: ${{ env.url }}"
echo "Test Suite: ${{ env.test_suite }}"


- name: Wait for Application to be Ready
run: |
echo "Waiting for application to be ready at ${{ env.url }} "
max_attempts=10
attempt=1

while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt: Checking if application is ready..."
if curl -f -s "${{ env.url }}" > /dev/null; then
echo "Application is ready!"
break

fi

if [ $attempt -eq $max_attempts ]; then
echo "Application is not ready after $max_attempts attempts"
exit 1
fi

echo "Application not ready, waiting 30 seconds..."
sleep 30
attempt=$((attempt + 1))
done

- name: Run tests(1)
id: test1
run: |
if [ "${{ env.test_suite }}" == "GoldenPath-Testing" ]; then
xvfb-run pytest -m gp --headed --html=report/report.html --self-contained-html
else
xvfb-run pytest --headed --html=report/report.html --self-contained-html
fi
working-directory: tests/e2e-test
continue-on-error: true

- name: Sleep for 30 seconds
if: ${{ steps.test1.outcome == 'failure' }}
run: sleep 30s
shell: bash

- name: Run tests(2)
id: test2
if: ${{ steps.test1.outcome == 'failure' }}
run: |
if [ "${{ env.test_suite }}" == "GoldenPath-Testing" ]; then
xvfb-run pytest -m gp --headed --html=report/report.html --self-contained-html
else
xvfb-run pytest --headed --html=report/report.html --self-contained-html
fi
working-directory: tests/e2e-test
continue-on-error: true

- name: Sleep for 60 seconds
if: ${{ steps.test2.outcome == 'failure' }}
run: sleep 60s
shell: bash

- name: Run tests(3)
id: test3
if: ${{ steps.test2.outcome == 'failure' }}
run: |
if [ "${{ env.test_suite }}" == "GoldenPath-Testing" ]; then
xvfb-run pytest -m gp --headed --html=report/report.html --self-contained-html
else
xvfb-run pytest --headed --html=report/report.html --self-contained-html
fi
working-directory: tests/e2e-test

- name: Upload test report
id: upload_report
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: test-report
path: tests/e2e-test/report/*

- name: Generate E2E Test Summary
if: always()
run: |
# Determine test suite type for title
if [ "${{ env.test_suite }}" == "GoldenPath-Testing" ]; then
echo "## 🧪 E2E Test Job Summary : Golden Path Testing" >> $GITHUB_STEP_SUMMARY
else
echo "## 🧪 E2E Test Job Summary : Smoke Testing" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY

# Determine overall test result
OVERALL_SUCCESS="${{ steps.test1.outcome == 'success' || steps.test2.outcome == 'success' || steps.test3.outcome == 'success' }}"
if [[ "$OVERALL_SUCCESS" == "true" ]]; then
echo "| **Job Status** | ✅ Success |" >> $GITHUB_STEP_SUMMARY
else
echo "| **Job Status** | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
fi

echo "| **Target URL** | [${{ env.url }}](${{ env.url }}) |" >> $GITHUB_STEP_SUMMARY
echo "| **Test Suite** | \`${{ env.test_suite }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Test Report** | [Download Artifact](${{ steps.upload_report.outputs.artifact-url }}) |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

echo "### 📋 Test Execution Details" >> $GITHUB_STEP_SUMMARY
echo "| Attempt | Status | Notes |" >> $GITHUB_STEP_SUMMARY
echo "|---------|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Test Run 1** | ${{ steps.test1.outcome == 'success' && '✅ Passed' || '❌ Failed' }} | Initial test execution |" >> $GITHUB_STEP_SUMMARY

if [[ "${{ steps.test1.outcome }}" == "failure" ]]; then
echo "| **Test Run 2** | ${{ steps.test2.outcome == 'success' && '✅ Passed' || steps.test2.outcome == 'failure' && '❌ Failed' || '⏸️ Skipped' }} | Retry after 30s delay |" >> $GITHUB_STEP_SUMMARY
fi

if [[ "${{ steps.test2.outcome }}" == "failure" ]]; then
echo "| **Test Run 3** | ${{ steps.test3.outcome == 'success' && '✅ Passed' || steps.test3.outcome == 'failure' && '❌ Failed' || '⏸️ Skipped' }} | Final retry after 60s delay |" >> $GITHUB_STEP_SUMMARY
fi

echo "" >> $GITHUB_STEP_SUMMARY

if [[ "$OVERALL_SUCCESS" == "true" ]]; then
echo "### ✅ Test Results" >> $GITHUB_STEP_SUMMARY
echo "- End-to-end tests completed successfully" >> $GITHUB_STEP_SUMMARY
echo "- Application is functioning as expected" >> $GITHUB_STEP_SUMMARY
else
echo "### ❌ Test Results" >> $GITHUB_STEP_SUMMARY
echo "- All test attempts failed" >> $GITHUB_STEP_SUMMARY
echo "- Check the e2e-test/test job for detailed error information" >> $GITHUB_STEP_SUMMARY
fi
3 changes: 3 additions & 0 deletions docs/CustomizingAzdParameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ By default this template will use the environment name as the prefix to prevent
| `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Specifies the GPT model version (allowed values: `2024-08-06`). |
| `AZURE_ENV_MODEL_CAPACITY` | integer | `30` | Sets the model capacity (choose based on your subscription's available GPT capacity). |
| `AZURE_ENV_IMAGETAG` | boolean | `latest` | Set the Image tag Like (allowed values: latest, dev, hotfix) |
| `AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT` | string | `cpscontainerreg.azurecr.io` | Sets the Azure Container Registry name (allowed value: `cpscontainerreg.azurecr.io`) |
| `AZURE_ENV_CONTAINER_IMAGE_TAG` | string | `latest` | Sets the container image tag (e.g., `latest`, `dev`, `hotfix`). |
| `AZURE_LOCATION` | string | `eastus` | Sets the primary Azure region for resource deployment. |
| `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Reuses an existing Log Analytics Workspace instead of provisioning a new one. |
| `AZURE_EXISTING_AI_PROJECT_RESOURCE_ID` | string | `<Existing AI Project resource Id>` | Reuses an existing AIFoundry and AIFoundryProject instead of creating a new one. |

Expand Down
13 changes: 8 additions & 5 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ param secondaryLocation string = (location == 'eastus2') ? 'westus2' : 'eastus2'
@description('Optional. The public container image endpoint.')
param publicContainerImageEndpoint string = 'cpscontainerreg.azurecr.io'

@description('Optional. The image tag for the container images.')
param imageTag string = 'latest'

@description('Optional. The resource group location.')
param resourceGroupLocation string = resourceGroup().location

Expand Down Expand Up @@ -717,7 +720,7 @@ module avmContainerApp 'br/public:avm/res/app/container-app:0.17.0' = {
containers: [
{
name: 'ca-${solutionSuffix}'
image: '${publicContainerImageEndpoint}/contentprocessor:latest'
image: '${publicContainerImageEndpoint}/contentprocessor:${imageTag}'

resources: {
cpu: '4'
Expand Down Expand Up @@ -766,7 +769,7 @@ module avmContainerApp_API 'br/public:avm/res/app/container-app:0.17.0' = {
containers: [
{
name: 'ca-${solutionSuffix}-api'
image: '${publicContainerImageEndpoint}/contentprocessorapi:latest'
image: '${publicContainerImageEndpoint}/contentprocessorapi:${imageTag}'
resources: {
cpu: '4'
memory: '8.0Gi'
Expand Down Expand Up @@ -896,7 +899,7 @@ module avmContainerApp_Web 'br/public:avm/res/app/container-app:0.17.0' = {
containers: [
{
name: 'ca-${solutionSuffix}-web'
image: '${publicContainerImageEndpoint}/contentprocessorweb:latest'
image: '${publicContainerImageEndpoint}/contentprocessorweb:${imageTag}'
resources: {
cpu: '4'
memory: '8.0Gi'
Expand Down Expand Up @@ -1190,7 +1193,7 @@ module avmContainerApp_update 'br/public:avm/res/app/container-app:0.17.0' = {
containers: [
{
name: 'ca-${solutionSuffix}'
image: '${publicContainerImageEndpoint}/contentprocessor:latest'
image: '${publicContainerImageEndpoint}/contentprocessor:${imageTag}'

resources: {
cpu: '4'
Expand Down Expand Up @@ -1250,7 +1253,7 @@ module avmContainerApp_API_update 'br/public:avm/res/app/container-app:0.17.0' =
containers: [
{
name: 'ca-${solutionSuffix}-api'
image: '${publicContainerImageEndpoint}/contentprocessorapi:latest'
image: '${publicContainerImageEndpoint}/contentprocessorapi:${imageTag}'
resources: {
cpu: '4'
memory: '8.0Gi'
Expand Down
9 changes: 9 additions & 0 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
},
"existingFoundryProjectResourceId": {
"value": "${AZURE_ENV_FOUNDRY_PROJECT_ID}"
},
"publicContainerImageEndpoint": {
"value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
},
"imageTag": {
"value": "${AZURE_ENV_CONTAINER_IMAGE_TAG}"
},
"location": {
"value": "${AZURE_LOCATION}"
}
}
}
9 changes: 9 additions & 0 deletions infra/main.waf.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@
},
"virtualMachineAdminPassword": {
"value": "${AZURE_ENV_VM_ADMIN_PASSWORD}"
},
"publicContainerImageEndpoint": {
"value": "${AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT}"
},
"imageTag": {
"value": "${AZURE_ENV_CONTAINER_IMAGE_TAG}"
},
"location": {
"value": "${AZURE_LOCATION}"
}
}
}
30 changes: 29 additions & 1 deletion tests/e2e-test/base/base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
"""
Base page module providing common functionality for all page objects.
"""


class BasePage:
"""Base class for all page objects with common methods."""

def __init__(self, page):
"""
Initialize the BasePage with a Playwright page instance.

Args:
page: Playwright page object
"""
self.page = page

def scroll_into_view(self, locator):
"""
Scroll the last element matching the locator into view.

Args:
locator: Playwright locator object
"""
reference_list = locator
locator.nth(reference_list.count() - 1).scroll_into_view_if_needed()

def is_visible(self, locator):
locator.is_visible()
"""
Check if an element is visible on the page.

Args:
locator: Playwright locator object

Returns:
bool: True if visible, False otherwise
"""
return locator.is_visible()
6 changes: 5 additions & 1 deletion tests/e2e-test/config/constants.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""
Configuration constants module for test environment settings.
"""

import os

from dotenv import load_dotenv

load_dotenv()
URL = os.getenv("url")
if URL.endswith("/"):
if URL and URL.endswith("/"):
URL = URL[:-1]
Loading
Loading