Skip to content

uitest-vscuse-others #95

uitest-vscuse-others

uitest-vscuse-others #95

name: uitest-vscuse-others
#
# Triggers:
# 1. Manual trigger (workflow_dispatch)
on:
workflow_dispatch:
inputs:
test_plan:
description: "Comma-separated list of test plan to run (e.g. DA_Regenrate_Action, Message_Extension_py_Local_Debug)"
required: false
image_tag:
description: "Docker image tag to use (e.g., 'latest', 'CY251212stable')"
required: true
default: "latest"
vscuse_version:
description: "VSCUSE Python package version to use (e.g., 'latest', 'v0.2.47')"
required: false
type: string
default: "latest"
email-receiver:
description: "email notification receiver"
required: false
type: string
max_retries:
description: "Maximum number of retry attempts (1-20, default: 7)"
required: false
type: number
default: 7
schedule_trigger:
description: "Whether the build is triggered by schedule"
type: boolean
default: false
permissions:
actions: read
jobs:
discover-test-plans:
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
test-plans: ${{ steps.get-plans.outputs.plans }}
email-receiver: ${{ steps.set-email.outputs.email-receiver }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Set email receiver
id: set-email
run: |
# Set email receiver based on trigger type
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event.inputs.schedule_trigger }}" == "true" ]; then
echo "[email protected];[email protected];[email protected]" >> $GITHUB_OUTPUT
echo "Schedule trigger: Using default email receivers"
elif [ -n "${{ github.event.inputs.email-receiver }}" ]; then
echo "email-receiver=${{ github.event.inputs.email-receiver }}" >> $GITHUB_OUTPUT
echo "Using user-provided email receiver: ${{ github.event.inputs.email-receiver }}"
else
echo "email-receiver=" >> $GITHUB_OUTPUT
echo "No email receiver specified"
fi
- name: Get test plans
id: get-plans
run: |
if [ -z "${{ github.event.inputs.test_plan }}" ]; then
# Get only top-level JSON files from plans directory (strip the .json extension)
plans=$(find packages/tests/vscuse/vscode-test-cases/plans/ -maxdepth 1 -type f \( -name "Feature_*.json" -o -name "Sample_*.json" \) -exec basename {} .json \; | jq -R -s -c 'split("\n")[:-1]')
echo "plans=$plans" >> $GITHUB_OUTPUT
echo "Found test plans: $plans"
else
# Use the input test_plan file name(s)
# Support comma-separated list, trim spaces
input_plans=$(echo "${{ github.event.inputs.test_plan }}" | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R -s -c 'split("\n")[:-1]')
echo "plans=$input_plans" >> $GITHUB_OUTPUT
echo "Using input test plans: $input_plans"
fi
main:
name: Case-${{ matrix.test_plan }}
needs: discover-test-plans
runs-on: ubuntu-latest
timeout-minutes: 80
strategy:
matrix:
test_plan: ${{ fromJson(needs.discover-test-plans.outputs.test-plans) }}
fail-fast: false
max-parallel: 100
permissions:
contents: read
packages: write
id-token: write
security-events: write
environment: engineering
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.TEST_TENANT_AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_API_KEY: ${{ secrets.TEST_TENANT_AZURE_OPENAI_KEY }}
AZURE_OPENAI_MODEL: "gpt-4.1"
# AZURE AI search
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: "text-embedding-ada-002"
AZURE_SEARCH_ENDPOINT: ${{ secrets.AZURE_SEARCH_ENDPOINT }}
AZURE_SEARCH_KEY: ${{ secrets.AZURE_SEARCH_KEY }}
# Azure Service Principal for M365 Toolkit authentication
#AZURE_CLIENT_ID: ${{ secrets.AZURE_SERVICE_PRINCIPAL_ID }}
#AZURE_CLIENT_SECRET: ${{ secrets.AZURE_SERVICE_PRINCIPAL_SECRET }}
AZURE_TENANT_ID: ${{ secrets.TEST_TENANT_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.TEST_TENANT_SUBSCRIPTION_ID }}
AZURE_AUTH_ENABLED: ${{ secrets.AZURE_SERVICE_PRINCIPAL_ID != '' && secrets.AZURE_SERVICE_PRINCIPAL_SECRET != '' && secrets.TEST_TENANT_TENANT_ID != '' }}
# M365 account for testing
M365_USERNAME_1: "[email protected]"
M365_USERNAME_2: "[email protected]"
M365_USERNAME_3: "[email protected]"
M365_USERNAME_4: "[email protected]"
M365_USERNAME_5: "[email protected]"
M365_USERNAME_6: "[email protected]"
M365_USERNAME_7: "[email protected]"
M365_USERNAME_8: "[email protected]"
M365_USERNAME_9: "[email protected]"
M365_USERNAME_10: "[email protected]"
M365_USERNAME_11: "[email protected]"
M365_USERNAME_12: "[email protected]"
M365_USERNAME_13: "[email protected]"
M365_USERNAME_14: "[email protected]"
M365_USERNAME_15: "[email protected]"
M365_USERNAME_16: "[email protected]"
M365_USERNAME_17: "[email protected]"
M365_ACCOUNT_PASSWORD: ${{ secrets.TEST_M365_PASSWORD }}
# The test account for Copilot features
M365_ACCOUNT_NAME_EnableCopilotAccess: ${{ secrets.TEST_TENANT_M365_ACCOUNT_NAME }}
M365_ACCOUNT_PASSWORD_EnableCopilotAccess: ${{ secrets.TEST_TENANT_M365_ACCOUNT_PASSWORD }}
# M365 admin account with Global admin role
M365_ACCOUNT_ADMIN_ACCOUNT: ${{ vars.TEST_TENANT_M365_ACCOUNT_ADMIN_ACCOUNT }}
M365_ACCOUNT_ADMIN_PASSWORD: ${{ secrets.TEST_TENANT_M365_ACCOUNT_ADMIN_PASSWORD }}
AZURE_ACCOUNT_NAME: ${{ secrets.TEST_TENANT_AZURE_ACCOUNT_NAME }}
AZURE_ACCOUNT_PASSWORD: ${{ secrets.TEST_TENANT_AZURE_ACCOUNT_PASSWORD }}
M365_TENANT_ID: ${{ secrets.TEST_CLEAN_TENANT_ID }}
# GITHUB test account
GITHUB_ACCOUNT_NAME: ${{ secrets.GH_ACCOUNT_NAME }}
GITHUB_ACCOUNT_PASSWORD: ${{ secrets.GH_ACCOUNT_PASSWORD }}
GITHUB_MFA_SECRET: ${{ secrets.GH_MFA_SECRET }}
#Additional value:
DA_MCP_ENTRA_SSO_CLIENTID: ${{ secrets.DA_MCP_ENTRA_SSO_CLIENTID }}
DA_MCP_OAUTH_CLIENT_SECERT: ${{ secrets.DA_MCP_OAUTH_CLIENT_SECERT }}
DA_MCP_OAUTH_CLIENTID: ${{ secrets.DA_MCP_OAUTH_CLIENTID }}
# SQL server
SQL_USER: ${{ secrets.SQL_USER }}
SECRET_SQL_PASSWORD: ${{ secrets.SECRET_SQL_PASSWORD }}
SQL_SERVER: "vscuse-test-sql.database.windows.net"
SQL_DATABASE: "vscuse-test-db"
# reddit
REDDIT_ID: "${{secrets.REDDIT_ID}}"
SECRET_REDDIT_PASSWORD: ${{secrets.SECRET_REDDIT_PASSWORD}}
# Foundry related parameters
AZURE_AI_FOUNDRY_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_FOUNDRY_PROJECT_ENDPOINT }}
AGENT_ID: ${{ secrets.AGENT_ID }}
# ms account
MS_AZURE_ACCOUNT_NAME: ${{vars.MS_AZURE_ACCOUNT_NAME}}
MS_AZURE_ACCOUNT_PASSWORD: ${{secrets.MS_AZURE_ACCOUNT_PASSWORD}}
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 22
- name: Install dependencies for download actions
run: npm install @octokit/auth-app @octokit/request @octokit/core node-fetch
shell: bash
- name: Run download script
run: node download-vscuse-python.js "${{ github.event.inputs.vscuse_version || 'latest' }}" "${{ github.workspace }}/packages/tests/vscuse/vscode-test-cases"
shell: bash
working-directory: .github/actions/setup-vscuse-environment
- name: Set up Python virtual environment and install wheel
run: |
cd packages/tests/vscuse/vscode-test-cases
python -m venv .venv
source .venv/bin/activate
WHEEL_FILE=$(ls *.whl | head -n 1)
pip install "./$WHEEL_FILE"
shell: bash
- name: Verify Azure Configuration
run: |
echo "Verifying Azure configuration..."
# Check Azure OpenAI (required)
if [ -z "$AZURE_OPENAI_ENDPOINT" ] || [ -z "$AZURE_OPENAI_API_KEY" ]; then
echo "❌ Azure OpenAI configuration missing"
exit 1
else
echo "✅ Azure OpenAI configured"
fi
# Check Azure Service Principal (optional)
if [ -n "$AZURE_CLIENT_ID" ] && [ -n "$AZURE_CLIENT_SECRET" ] && [ -n "$AZURE_TENANT_ID" ]; then
echo "✅ Azure Service Principal configured - M365 Toolkit authentication enabled"
else
echo "⚠️ Azure Service Principal not configured - M365 Toolkit authentication disabled"
fi
- name: Set m365 account randomly
run: |
users=("${{ env.M365_USERNAME_1 }}" "${{ env.M365_USERNAME_2 }}" "${{ env.M365_USERNAME_3 }}" "${{ env.M365_USERNAME_4 }}" "${{ env.M365_USERNAME_5 }}" "${{ env.M365_USERNAME_6 }}" "${{ env.M365_USERNAME_7 }}" "${{ env.M365_USERNAME_8 }}" "${{ env.M365_USERNAME_9 }}" "${{ env.M365_USERNAME_10 }}" "${{ env.M365_USERNAME_11 }}" "${{ env.M365_USERNAME_12 }}" "${{ env.M365_USERNAME_13 }}" "${{ env.M365_USERNAME_14 }}" "${{ env.M365_USERNAME_15 }}" "${{ env.M365_USERNAME_16 }}" "${{ env.M365_USERNAME_17 }}")
count=${#users[@]}
index=$((RANDOM%$count))
echo "account index: $index"
echo "M365_ACCOUNT_NAME=${users[index]}" >> $GITHUB_ENV
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull pre-built Docker image
run: |
IMAGE_TAG="${{ github.event.inputs.image_tag }}"
if [ -z "$IMAGE_TAG" ]; then
IMAGE_TAG="latest"
fi
VSCUSE_VSCODE_IMAGE="ghcr.io/officedev/vscuse-atk-vscode:$IMAGE_TAG"
echo "VSCUSE_VSCODE_IMAGE=$VSCUSE_VSCODE_IMAGE" >> $GITHUB_ENV
echo "Pulling pre-built Docker image: $VSCUSE_VSCODE_IMAGE"
docker pull $VSCUSE_VSCODE_IMAGE
echo "✅ Image pulled successfully!"
echo "Image details:"
docker images $VSCUSE_VSCODE_IMAGE
- name: Run test
run: |
echo "🚀 Running test plan: ${{ matrix.test_plan }}"
echo "Using image tag: ${{ github.event.inputs.image_tag || 'latest' }}"
echo "Test execution started at: $(date)"
cd packages/tests/vscuse/vscode-test-cases
python -m venv .venv
source .venv/bin/activate
vscuse execute --config-file ./config.yaml --groups-dir groups ./plans/${{ matrix.test_plan }}.json
- name: Rename test_report.html to index.html
if: always()
run: |
REPORT_DIR="packages/tests/vscuse/vscode-test-cases/test_report"
if [ -f "$REPORT_DIR/test_report.html" ]; then
mv "$REPORT_DIR/test_report.html" "$REPORT_DIR/index.html"
echo "Renamed test_report.html to index.html"
else
echo "No test_report.html found to rename"
fi
- name: Encrypt and zip test report
if: always()
run: |
REPORT_DIR="packages/tests/vscuse/vscode-test-cases/test_report"
ZIP_FILE="test-report-${{ matrix.test_plan }}-${{ github.run_number }}.zip"
if [ -z "${{ secrets.ARTIFACT_ZIP_PASSWORD }}" ]; then
echo "❌ Error: ARTIFACT_ZIP_PASSWORD secret is not set"
echo "Please configure the ARTIFACT_ZIP_PASSWORD secret in repository settings"
exit 1
fi
# Install zip if not available
sudo apt-get update && sudo apt-get install -y zip
# Create encrypted zip file in the report directory
cd "$REPORT_DIR"
zip -e -P "${{ secrets.ARTIFACT_ZIP_PASSWORD }}" "$ZIP_FILE" index.html
cd -
echo "✅ Created encrypted zip: $REPORT_DIR/$ZIP_FILE"
echo "ZIP_FILE=$ZIP_FILE" >> $GITHUB_ENV
shell: bash
- name: Upload encrypted test report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report-${{ matrix.test_plan }}-${{ github.run_number }}
path: packages/tests/vscuse/vscode-test-cases/test_report/${{ env.ZIP_FILE }}
retention-days: 7
if-no-files-found: ignore
# Upload files to Azure Blob Storage
- name : Login to Azure
if: always()
uses: azure/login@v2
with:
client-id: ${{secrets.DEVOPS_CLIENT_ID}}
tenant-id: ${{secrets.DEVOPS_TENANT_ID}}
subscription-id: ${{secrets.DEVOPS_SUB_ID}}
enable-AzPSSession: true
- name: 🌐Upload files to Azure Blob Storage
if: always()
shell: pwsh
run: |
$guid = [guid]::NewGuid().ToString()
$account = $env:AZURE_STORAGE_ACCOUNT
$sourcePath = "packages/tests/vscuse/vscode-test-cases/test_report/"
$destination = "`content/$guid"
$storageUrl = "https://storproxy-app-voazuxhhvtgiq.azurewebsites.net/$guid/index.html"
Write-Host "🌐Here is the report: $storageUrl"
echo "$storageUrl" > storage_url.txt
az storage blob upload-batch `
--account-name $account `
--auth-mode login `
--destination $destination `
--source "$sourcePath" `
--overwrite
env:
AZURE_STORAGE_ACCOUNT: storproxystvoazuxhhvtgiq
- name: Upload storage URL artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: storage-url-${{ matrix.test_plan }}-${{ github.run_id }}
path: storage_url.txt
retention-days: 7
if-no-files-found: ignore
- name: Cleanup
if: always()
run: |
echo "🧹 Cleaning up resources..."
# Stop and remove any containers using our image
echo "Cleaning up VSCode containers..."
docker stop $(docker ps -q --filter "ancestor=ghcr.io/officedev/vscuse-atk-vscode:${{ github.event.inputs.image_tag || 'latest' }}") 2>/dev/null || echo "No containers to stop"
docker rm $(docker ps -aq --filter "ancestor=ghcr.io/officedev/vscuse-atk-vscode:${{ github.event.inputs.image_tag || 'latest' }}") 2>/dev/null || echo "No containers to remove"
rerun:
permissions:
actions: write
needs: main
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true') && failure() && github.run_attempt < (github.event.inputs.max_retries || 7) }}
runs-on: ubuntu-latest
env:
MAX_RETRIES: ${{ github.event.inputs.max_retries || 7 }}
M365_ACCOUNT_PASSWORD: ${{ secrets.TEST_M365_PASSWORD }}
M365_ACCOUNT_NAME: "[email protected]"
CLEAN_CLIENT_ID: ${{ secrets.TEST_CLEAN_CLIENT_ID }}
CLEAN_CLIENT_SECRET: ${{ secrets.TEST_CLEAN_CLIENT_SECRET }}
CLEAN_TENANT_ID: ${{ secrets.TEST_CLEAN_TENANT_ID }}
AZURE_TENANT_ID: ${{ secrets.TEST_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.TEST_SUBSCRIPTION_ID }}
AZURE_ACCOUNT_NAME: ${{ secrets.TEST_USER_NAME }}
AZURE_ACCOUNT_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
steps:
- name: Checkout
if: ${{ github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup node
if: ${{ github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' }}
uses: actions/setup-node@v3
with:
node-version: 22
- uses: pnpm/action-setup@v4
if: ${{ github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' }}
- name: Setup project
if: ${{ github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' }}
working-directory: ./
run: |
npm run setup:e2e
- name: Clean resources
if: ${{ github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' }}
working-directory: packages/tests
run: |
npx ts-node src/scripts/clean.ts
- name: Calculate max retries
id: calc-retries
run: |
# Get max retries from input, default to 7, cap at 20
max_retries=${{ github.event.inputs.max_retries || 7 }}
if [ "$max_retries" -gt 20 ]; then
max_retries=20
fi
if [ "$max_retries" -lt 1 ]; then
max_retries=1
fi
echo "max_retries=$max_retries" >> $GITHUB_OUTPUT
echo "Using max retries: $max_retries"
- name: trigger rerun workflow
run: |
curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"\
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/rerun.yml/dispatches \
-d '{"ref":"${{ github.ref_name }}","inputs":{"run_id":"${{ github.run_id }}", "max_attempts":"${{ steps.calc-retries.outputs.max_retries }}"}}'
report:
# Only send email on the last attempt or when all tests pass
# Send email if: (1) triggered by schedule or schedule_trigger is true, OR (2) triggered manually with email-receiver input provided
if: ${{ always() && (github.event_name == 'schedule' || github.event.inputs.schedule_trigger == 'true' || needs.discover-test-plans.outputs.email-receiver != '') && (success() || github.run_attempt >= (github.event.inputs.max_retries || 7)) }}
needs: [discover-test-plans, main]
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/tests/vscuse
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Download storage URLs
uses: actions/download-artifact@v4
with:
pattern: storage-url-*
path: /tmp/storage-urls
- name: Process storage URLs
shell: bash
run: |
if [ -d "/tmp/storage-urls" ]; then
echo "Found storage URLs folder. Processing contents..."
# Find and extract all zip files to /tmp/storage-urls directory
find /tmp/storage-urls -name "*.zip" -exec unzip -o {} -d /tmp/storage-urls \;
# Create a JSON object from all storage_url.txt files
echo "{" > /tmp/url_mapping.json
first_entry=true
# Find all storage_url.txt files and process them
find /tmp/storage-urls -type f -name "storage_url.txt" | while read file; do
# Extract the test_plan and run_id from the parent directory name (storage-url-TESTPLAN-RUNID)
dir_name=$(basename $(dirname "$file"))
# Format: storage-url-{test_plan}-{run_id}
# Extract test_plan (everything between 'storage-url-' and the last '-{run_id}')
run_id=$(echo "$dir_name" | rev | cut -d'-' -f1 | rev)
test_plan=$(echo "$dir_name" | sed "s/^storage-url-//" | sed "s/-${run_id}$//")
case_run_id="${test_plan}-${run_id}"
echo "Processing: dir_name=$dir_name, test_plan=$test_plan, run_id=$run_id, case_run_id=$case_run_id"
# Read the content of the file
content=$(cat "$file" | tr -d '\n' | tr -d '\r')
# Add comma for all entries except the first one
if [ "$first_entry" = true ]; then
first_entry=false
else
echo "," >> /tmp/url_mapping.json
fi
# Add the entry to JSON (properly escaped)
echo " \"$case_run_id\": \"$content\"" >> /tmp/url_mapping.json
done
echo "}" >> /tmp/url_mapping.json
echo "Generated JSON mapping:"
cat /tmp/url_mapping.json
# Count total files processed
total_urls=$(find /tmp/storage-urls -type f -name "storage_url.txt" | wc -l)
echo "Total storage URL files processed: $total_urls"
else
echo "No storage URL artifacts found"
echo "{}" > /tmp/url_mapping.json
fi
- name: Install Dateutils
run: |
sudo apt install dateutils
- name: list jobs
id: list-jobs
working-directory: packages/tests/vscuse
env:
AZURE_DEVOPS_PAT: ${{ secrets.AZURE_DEVOPS_PAT }}
AZURE_DEVOPS_ORG: "msazure"
AZURE_DEVOPS_PROJECT: "Microsoft%20Teams%20Extensibility"
run: |
page=1
jobs="[]"
echo "Initial jobs: $jobs"
report_map_json_file_path="/tmp/url_mapping.json"
# Function to query Azure DevOps for bugs with specific tag
query_bugs_by_tag() {
local tag="$1"
local encoded_tag=$(echo "$tag" | sed 's/ /%20/g')
# WIQL query to find active bugs with the specific tag
local wiql_query="{\"query\": \"SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.WorkItemType] = 'Bug' AND [System.State] IN ('Active','New','Committed') AND [System.Tags] CONTAINS '${tag}'\"}"
local response=$(curl -s -u ":${AZURE_DEVOPS_PAT}" \
-H "Content-Type: application/json" \
-X POST \
"https://dev.azure.com/${AZURE_DEVOPS_ORG}/${AZURE_DEVOPS_PROJECT}/_apis/wit/wiql?api-version=7.0" \
-d "$wiql_query")
echo "$response"
}
while :
do
url="https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}/jobs?per_page=80&page=$page"
echo "Fetching URL: $url"
resp=$(curl -sf -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "$url")
echo "Response from GitHub API: $resp"
new_jobs=$(echo "$resp" | jq -cr '.jobs')
echo "New jobs extracted: $new_jobs"
jobs=$(jq -cr --slurp 'add' <(echo "$jobs") <(echo "$new_jobs"))
echo "Updated jobs list: $jobs"
has_next=$(curl -sfI -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "$url" | grep -Fi "link:" | grep "rel=\"next\"" || true)
echo "Has next page: $has_next"
if [ -z "$has_next" ]; then
break
fi
page=$((page+1))
done
echo "Final job list: $jobs"
cases=$(echo "$jobs" | jq -r '.[] | select(.name | contains("Case-")) | .name')
echo "Extracted case names: $cases"
passed=0
failed=0
failedlimit=100
passedlimit=100
failedlist=""
passedlist=""
lists=""
emails="${{ needs.discover-test-plans.outputs.email-receiver }}"
echo "Initial email list: $emails"
while IFS= read -r case;
do
if [ -z "$case" ]; then
continue
fi
echo "Processing case: $case"
name=$(echo "$case" | awk -F '|' '{print $1}')
case_id=$(echo "$name" | awk -F '-' '{print $2}')
os=$(echo "$case" | awk -F '|' '{print $2}')
node=$(echo "$case" | awk -F '|' '{print $3}')
branch=$(echo "$case" | awk -F '|' '{print $4}')
title="Unknown Test Case Title"
author="N/A"
work_item_url="N/A"
owner="N/A"
# Extract the title from the plan JSON file
plan_file="./vscode-test-cases/plans/${case_id}.json"
echo "Looking for plan file: $plan_file"
if [ -f "$plan_file" ]; then
echo "Found plan file: $plan_file"
# Get title from plan_metadata.name
title=$(jq -r '.plan_metadata.name // "Unknown Test Case Title"' "$plan_file")
echo "Extracted title: $title"
# Get owner from plan_metadata.description.owner
owner=$(jq -r '.plan_metadata.description.owner // "N/A"' "$plan_file")
echo "Extracted owner: $owner"
# Get work item IDs from plan_metadata.description.workitem
# Workitem may contain multiple work item IDs separated by comma, space, or semicolon
workitem=$(jq -r '.plan_metadata.description.workitem // ""' "$plan_file")
if [ -n "$workitem" ] && [ "$workitem" != "null" ]; then
# Split by comma, space, or semicolon and process each work item ID
work_item_links=""
# Replace commas and semicolons with spaces, then iterate
for item_id in $(echo "$workitem" | tr ',;' ' '); do
# Trim whitespace
item_id=$(echo "$item_id" | xargs)
if [ -n "$item_id" ]; then
# If it looks like a work item ID (numeric), create a link
if [[ "$item_id" =~ ^[0-9]+$ ]]; then
if [ -n "$work_item_links" ]; then
work_item_links="$work_item_links, "
fi
work_item_links="$work_item_links<a href=\\\"https://msazure.visualstudio.com/Microsoft%20Teams%20Extensibility/_workitems/edit/$item_id\\\">$item_id</a>"
fi
fi
done
if [ -n "$work_item_links" ]; then
work_item_url="$work_item_links"
else
work_item_url="$workitem"
fi
fi
echo "Extracted work items: $work_item_url"
else
echo "Plan file not found: $plan_file"
# Fallback: use case_id as title with underscores replaced by spaces
title=$(echo "$case_id" | tr '_' ' ')
fi
echo "Extracted info - Name: $name, OS: $os, Node: $node, Branch: $branch, Case ID: $case_id, Title: $title, Author: $author, Work Item URL: $work_item_url"
# file=$(find src -name "$name.test.ts" 2>/dev/null)
# echo "Test file found: $file"
status=$(echo "$jobs" | jq --arg case "$case" -r '.[] | select(.name == $case ) | .conclusion')
echo "Job status: $status"
run_id=$(echo "$jobs" | jq --arg case "$case" -r '.[] | select(.name == $case ) | .run_id')
echo "Run Id: $run_id"
# Combine the case name and run_id to create a lookup key
lookup_key="${case_id}-${run_id}"
echo "Lookup Key: $lookup_key"
# Get the report URL from the JSON file using the lookup_key
report_url=$(jq -r --arg key "$lookup_key" '.[$key]' "$report_map_json_file_path")
if [ -z "$report_url" ] || [ "$report_url" = "null" ]; then
report_url="N/A"
else
report_url="<a href=\\\"$report_url\\\">Report</a>"
fi
echo "Report URL: $report_url"
if [[ -n "$email" && ! "$emails" == *"$email"* && "$status" == "failure" ]]; then
emails="$emails;$email;"
fi
echo "Updated email list: $emails"
started_at=$(echo "$jobs" | jq --arg case "$case" -r '.[] | select(.name == $case ) | .started_at')
completed_at=$(echo "$jobs" | jq --arg case "$case" -r '.[] | select(.name == $case ) | .completed_at')
echo "Start time: $started_at, Completed time: $completed_at"
duration=$(dateutils.ddiff "$started_at" "$completed_at" -f "%Mm %Ss" 2>/dev/null)
echo "Calculated duration: $duration"
label=""
if [ "$status" == "success" ]; then
passed=$((passed+1))
label="<span style=\\\"background-color:#2aa198;color:white;font-weight:bold;\\\">PASSED</span>"
else
failed=$((failed+1))
label="<span style=\\\"background-color: #dc322f;color:white;font-weight:bold;\\\">FAILED</span>"
fi
echo "Job result label: $label"
url=$(echo "$jobs" | jq --arg case "$case" -r '.[] | select(.name == $case ) | .html_url')
display_name=$(echo "$name" | sed 's/^Case-//')
url="<a href=\\\"$url\\\">$display_name</a>"
echo "Job URL: $url"
linked_bugs=""
# Query Azure DevOps for linked bugs if the case failed
if [ "$status" != "success" ]; then
echo "Case failed, querying Azure DevOps for bugs with tag: $display_name"
# Try to query Azure DevOps, with error handling
bug_response=""
query_error=""
if [ -z "$AZURE_DEVOPS_PAT" ]; then
query_error="⚠️No ADO PAT configured"
echo "Azure DevOps PAT not configured, skipping bug query"
else
bug_response=$(query_bugs_by_tag "$display_name" 2>&1) || query_error="⚠️Network issue"
echo "Bug query response: $bug_response"
# Check if the response is valid JSON and contains workItems
if [ -z "$query_error" ]; then
# Check for API errors in response
api_error=$(echo "$bug_response" | jq -r '.message // empty' 2>/dev/null)
if [ -n "$api_error" ]; then
query_error="⚠️ADO API error"
echo "Azure DevOps API error: $api_error"
fi
fi
fi
if [ -n "$query_error" ]; then
linked_bugs="$query_error"
echo "Bug query failed: $query_error"
else
# Extract work item IDs from the response
bug_ids=$(echo "$bug_response" | jq -r '.workItems[]?.id // empty' 2>/dev/null)
if [ -n "$bug_ids" ]; then
bug_links=""
for bug_id in $bug_ids; do
if [ -n "$bug_links" ]; then
bug_links="$bug_links, "
fi
bug_links="$bug_links<a href=\\\"https://dev.azure.com/${AZURE_DEVOPS_ORG}/${AZURE_DEVOPS_PROJECT}/_workitems/edit/$bug_id\\\">🐞$bug_id</a>"
done
linked_bugs="$bug_links"
echo "Found linked bugs: $linked_bugs"
else
linked_bugs="❌Failed but no bug yet"
echo "No linked bugs found for failed case"
fi
fi
fi
row="<tr> <td style=\\\"text-align: left;\\\">$url</td> <td style=\\\"text-align: left;\\\">$title</td> <td style=\\\"text-align: center;\\\">$label</td> <td style=\\\"text-align: center;\\\">$owner</td> <td style=\\\"text-align: center;\\\">$work_item_url</td> <td style=\\\"text-align: center;\\\">$linked_bugs</td> <td style=\\\"text-align: center;\\\">$duration</td> <td style=\\\"text-align: center;\\\">$report_url</td> </tr>"
echo "Generated row: $row"
if [[ "$status" == "success" && $passed -lt $passedlimit ]]; then
passedlist="$passedlist $row"
elif [[ "$status" != "success" && $failed -lt $failedlimit ]]; then
failedlist="$failedlist $row"
fi
done <<< "$cases"
lists="$failedlist $passedlist"
echo "Final failed list: $failedlist"
echo "Final passed list: $passedlist"
body="<a href=\\\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\\\">Test report link.</a> <br/> <table class=\\\"w3-table w3-striped w3-bordered\\\"> <tr> <th>CASE</th> <th>TITLE</th> <th>STATUS</th> <th>OWNER</th> <th>WORK ITEM</th> <th>LINKED BUG(S)</th> <th>DURATION</th> <th>REPORT</th> </tr> $lists </table> <br />"
echo "Generated email body: $body"
total=$((passed+failed))
echo "Total jobs: $total, Passed: $passed, Failed: $failed"
subject="Vsc Use UI Test Report [Samples + Features] ($passed/$total Passed)"
if [ $failed -gt 0 ]; then
subject="[FAILED] $subject"
else
subject="[PASSED] $subject"
fi
echo "Final subject: $subject"
echo "body=$body" >> $GITHUB_OUTPUT
echo "to=$emails" >> $GITHUB_OUTPUT
echo "subject=$subject" >> $GITHUB_OUTPUT
- name: Prepare email body file
if: always()
run: |
printf '%s' "${{ steps.list-jobs.outputs.body }}" > email-body.html
echo "BODY_FILE=$(pwd)/email-body.html" >> $GITHUB_ENV
- name: Send E-mail
uses: ./.github/actions/send-email-report-vscuse
env:
TO: ${{ steps.list-jobs.outputs.to }}
SUBJECT: ${{ steps.list-jobs.outputs.subject }}
MAIL_CLIENT_ID: ${{ secrets.TEST_CLEAN_CLIENT_ID }}
MAIL_CLIENT_SECRET: ${{ secrets.TEST_CLEAN_CLIENT_SECRET }}
MAIL_TENANT_ID: ${{ secrets.TEST_CLEAN_TENANT_ID }}