From e8f5b8946ef6b690d11f4b8acae7d9e3d394d19e Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 15:33:48 -0500 Subject: [PATCH 01/40] Updated to have timeout Jeremy suggested --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6bddeece..d1923e11 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -127,6 +127,7 @@ stages: - job: download_and_merge_reports dependsOn: playwright_tests + timeoutInMinutes: 8 steps: - task: DownloadPipelineArtifact@2 inputs: From 188cafce16d0e7bfc62ca163e6918495abb59313 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 16:20:43 -0500 Subject: [PATCH 02/40] Changed what variable is accessed --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d1923e11..c57f0459 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -158,8 +158,8 @@ stages: # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - echo \"Writing report to Jira card '$(Build.SourceBranchName)'...\" && - ./devops/scripts/jira_writeback.sh add_comment \"$(Build.SourceBranchName)\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + echo \"Writing report to Jira card '$(System.PullRequest.SourceBranchName)'...\" && + ./devops/scripts/jira_writeback.sh add_comment \"$(System.PullRequest.SourceBranchName)\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") # Start container and stream logs echo "Starting merge" From 853c6743b4fb9fc51faf2c03d033acd903a8a189 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 16:21:32 -0500 Subject: [PATCH 03/40] Temporarily changed number of shards for testing --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c57f0459..3a647033 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,7 +36,7 @@ variables: - name: imageTag value: '$(Build.BuildId)' - name: totalShards - value: 4 + value: 50 #TODO: Change back before merge stages: # PR's From 2bd2ffcabbdc9fd347be04b74a1f24c3918838bd Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 16:38:01 -0500 Subject: [PATCH 04/40] Extract Jira Card Number from SourceBranchName --- azure-pipelines.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3a647033..904fb5dc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -157,9 +157,11 @@ stages: # echo \"Number of test failures: '$num_failures'\" # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later - echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - echo \"Writing report to Jira card '$(System.PullRequest.SourceBranchName)'...\" && - ./devops/scripts/jira_writeback.sh add_comment \"$(System.PullRequest.SourceBranchName)\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + echo \"Contents of ortoni-report:\" && ls ./ortoni-report && + branch_name="$(System.PullRequest.SourceBranch)" + jira_card_number="${branch_name##*/}" + echo \"Writing report to Jira card '$jira_card_number'...\" && + ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") # Start container and stream logs echo "Starting merge" From ef921f2d5d2b94caf9a6b2a7982a0a84bc079b45 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 16:54:57 -0500 Subject: [PATCH 05/40] Changed the way we get source branch name --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 904fb5dc..d2e60ca8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -158,8 +158,8 @@ stages: # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - branch_name="$(System.PullRequest.SourceBranch)" - jira_card_number="${branch_name##*/}" + branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always "merge" on PR + jira_card_number="${branch_name##*/}" # Grab everything after last "/"" echo \"Writing report to Jira card '$jira_card_number'...\" && ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") From bce8824f313fb70cf5621c2090e071a585fd1b0e Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 4 Mar 2025 17:35:23 -0500 Subject: [PATCH 06/40] Escaped quotes --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d2e60ca8..0366cb7d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -159,7 +159,7 @@ stages: # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always "merge" on PR - jira_card_number="${branch_name##*/}" # Grab everything after last "/"" + jira_card_number=\"${branch_name##*/}\" # Grab everything after last "/"" echo \"Writing report to Jira card '$jira_card_number'...\" && ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") From 0728b81d066ac7d89453879d58e95d1d857e1aac Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 09:03:12 -0500 Subject: [PATCH 07/40] Added timeout for tasks that run even when canceled --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0366cb7d..045873e7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -128,6 +128,7 @@ stages: - job: download_and_merge_reports dependsOn: playwright_tests timeoutInMinutes: 8 + cancelTimeoutInMinutes: 10 steps: - task: DownloadPipelineArtifact@2 inputs: From 9d6f582be603bf6b58af98f64af16798184692cf Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 09:20:09 -0500 Subject: [PATCH 08/40] Fixed unescaped quotes --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 045873e7..802cee99 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -159,8 +159,8 @@ stages: # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always "merge" on PR - jira_card_number=\"${branch_name##*/}\" # Grab everything after last "/"" + branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' echo \"Writing report to Jira card '$jira_card_number'...\" && ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") From e59efb543ada6b649c04eaeeb7f4b6323477e9d1 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 09:35:13 -0500 Subject: [PATCH 09/40] Added logging --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 802cee99..48984cac 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -160,6 +160,7 @@ stages: # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + echo \"Retrieved branch name: '$branch_name'\" jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' echo \"Writing report to Jira card '$jira_card_number'...\" && ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") From 132c58db22dad6d80b59a97428d9bd15e2533e46 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 10:00:10 -0500 Subject: [PATCH 10/40] Changed the way we grab source branch name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 48984cac..7b210278 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -159,7 +159,7 @@ stages: # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - branch_name=$(git rev-parse --abbrev-ref HEAD) # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + branch_name=$(git name-rev --name-only --refs=\"refs/remotes/origin/*\" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR echo \"Retrieved branch name: '$branch_name'\" jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' echo \"Writing report to Jira card '$jira_card_number'...\" && From 1bea6b2aa5002de0adb5ecad54813d94f6b01344 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 10:08:12 -0500 Subject: [PATCH 11/40] Fixed pipeline --- azure-pipelines.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 40542015..7b210278 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -90,7 +90,6 @@ stages: "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@smoke | @Advanced\"") # Start container and stream logs - echo "Starting tests for shard $(shardNumber)..." docker start -a $container_id @@ -127,8 +126,6 @@ stages: condition: always() - job: download_and_merge_reports - container: - image: mcr.microsoft.com/playwright:v1.48.0-noble dependsOn: playwright_tests timeoutInMinutes: 8 cancelTimeoutInMinutes: 10 @@ -195,19 +192,23 @@ stages: # inputs: # searchFolder: 'test-results' # testResultsFormat: 'JUnit' - # testResultsFiles: 'results.xml' + # testResultsFiles: 'junit_results.xml' # mergeTestResults: true # failTaskOnFailedTests: false # testRunTitle: 'Playwright Tests' # condition: succeededOrFailed() - - task: PublishPipelineArtifact@1 - displayName: 'Publish Merged Report' - condition: always() - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' - artifact: 'playwright-merged-report' - publishLocation: 'pipeline' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Merged Report' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' + artifact: 'playwright-merged-report' + publishLocation: 'pipeline' + - script: | + docker rmi $(dockerImageName):$(imageTag) -f + displayName: 'Cleanup Docker Image' + condition: always() - ${{ else }}: # Dev Build/Deploy From b1cbed58f8ae9c787ca9a872529c49f019446f15 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 10:26:47 -0500 Subject: [PATCH 12/40] Moved grabbing branch name out of container --- azure-pipelines.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7b210278..a79fcd0f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -142,12 +142,16 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - bash: | + branch_name=$(git name-rev --name-only --refs=\"refs/remotes/origin/*\" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + echo \"Retrieved branch name: '$branch_name'\" + jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' # Create container for jira writeback container_id=$(docker create \ --ipc=host \ -e JIRA_SERVER=$(JIRA_SERVER) \ -e JIRA_USERNAME=$(JIRA_USERNAME) \ -e JIRA_API_KEY=$(JIRA_API_KEY) \ + -e JIRA_CARD_NUMBER=$jira_card_number \ $(dockerImageName):$(imageTag) \ bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && @@ -159,11 +163,8 @@ stages: # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - branch_name=$(git name-rev --name-only --refs=\"refs/remotes/origin/*\" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR - echo \"Retrieved branch name: '$branch_name'\" - jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' - echo \"Writing report to Jira card '$jira_card_number'...\" && - ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + echo \"Writing report to Jira card '$JIRA_CARD_NUMBER'...\" && + ./devops/scripts/jira_writeback.sh add_comment \"$JIRA_CARD_NUMBER\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") # Start container and stream logs echo "Starting merge" From 057727e9aced5613f6eaeddc4fac562dee7e86fc Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 10:38:59 -0500 Subject: [PATCH 13/40] Fixed quote escaping --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a79fcd0f..c3a86377 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -142,9 +142,9 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - bash: | - branch_name=$(git name-rev --name-only --refs=\"refs/remotes/origin/*\" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR - echo \"Retrieved branch name: '$branch_name'\" - jira_card_number=\"${branch_name##*/}\" # Grab everything after last '/' + branch_name=$(git name-rev --name-only --refs="refs/remotes/origin/*" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + echo "Retrieved branch name: '$branch_name'" + jira_card_number="${branch_name##*/}" # Grab everything after last '/' # Create container for jira writeback container_id=$(docker create \ --ipc=host \ From 745d7322cd3cf3b713ef31ff1f7f598ff5b09dd8 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 10:57:27 -0500 Subject: [PATCH 14/40] Attempting to grab source branch name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3a86377..7a521784 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -142,7 +142,7 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - bash: | - branch_name=$(git name-rev --name-only --refs="refs/remotes/origin/*" HEAD^1 | sed 's/~.*//') # Had to do it this way because Build.SourceBranchName is always 'merge' on PR + branch_name=$(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" jira_card_number="${branch_name##*/}" # Grab everything after last '/' # Create container for jira writeback From 563dabc0e9c0367ad23370a462b21eaf5e1ec588 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 11:51:17 -0500 Subject: [PATCH 15/40] Added more logging and removed comments --- azure-pipelines.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7a521784..5f3b959d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -144,27 +144,23 @@ stages: - bash: | branch_name=$(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" - jira_card_number="${branch_name##*/}" # Grab everything after last '/' + jira_card_number="${branch_name##*/}" + echo "Extracted JIRA Card number: '$jira_card_number'" # Create container for jira writeback container_id=$(docker create \ --ipc=host \ -e JIRA_SERVER=$(JIRA_SERVER) \ -e JIRA_USERNAME=$(JIRA_USERNAME) \ -e JIRA_API_KEY=$(JIRA_API_KEY) \ - -e JIRA_CARD_NUMBER=$jira_card_number \ + -e jira_card_number="$jira_card_number" \ $(dockerImageName):$(imageTag) \ bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && echo \"Merging reports...\" && npx playwright merge-reports --reporter=ortoni-report ./playwright-reports - # npx playwright merge-reports --config=playwright-tests/playwright.config.ts ./playwright-reports - # num_failures=$(sed -n 's/.*failures="\([^"]*\)".*/\1/p' /app/test-results/junit_results.xml | head -n 1) - # echo \"Number of test failures: '$num_failures'\" - # (($num_failures -eq 0)) && jira_message='Playwright tests passed!' || jira_message=\"$num_failures Playwright tests failed.\" - # TODO: Update to include test status message later echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - echo \"Writing report to Jira card '$JIRA_CARD_NUMBER'...\" && - ./devops/scripts/jira_writeback.sh add_comment \"$JIRA_CARD_NUMBER\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + echo \"Writing report to Jira card '$jira_card_number'...\" && + ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") # Start container and stream logs echo "Starting merge" From b3856aebb83f5805295aab8fa01335bd31846588 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 12:10:15 -0500 Subject: [PATCH 16/40] Added more logging --- azure-pipelines.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5f3b959d..e4cb2666 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -159,8 +159,11 @@ stages: echo \"Merging reports...\" && npx playwright merge-reports --reporter=ortoni-report ./playwright-reports echo \"Contents of ortoni-report:\" && ls ./ortoni-report && + echo 'Current dir: ' && pwd + echo 'Contents of current dir: ' && ls + echo 'Contents of /app/devops/scripts' && ls /app/devops/scripts echo \"Writing report to Jira card '$jira_card_number'...\" && - ./devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") # Start container and stream logs echo "Starting merge" From a2316132b1ffe68cfcbdeced641414ebd6a9eede Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 12:21:28 -0500 Subject: [PATCH 17/40] Added jira writeback script --- devops/scripts/jira_writeback.sh | 149 +++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 devops/scripts/jira_writeback.sh diff --git a/devops/scripts/jira_writeback.sh b/devops/scripts/jira_writeback.sh new file mode 100755 index 00000000..1d605072 --- /dev/null +++ b/devops/scripts/jira_writeback.sh @@ -0,0 +1,149 @@ +#!/bin/bash +create_issue() { + local title="$1" + local project_key="$2" + local issue_type="$3" + local parent_issue_key="$4" + + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local parent_issue=$(curl -s -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$parent_issue_key") + + local parent_fix_versions=$(echo $parent_issue | jq -r '.fields.fixVersions') + + local created_issue=$(curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "fields": { + "summary": "'"$title"'", + "project": { + "key": "'"$project_key"'" + }, + "issuetype": { + "name": "'"$issue_type"'" + }, + "parent": { + "key": "'"$parent_issue_key"'" + }, + "fixVersions": '"$parent_fix_versions"' + } + }' \ + "$JIRA_SERVER/rest/api/3/issue") + + local created_issue_id=$(echo $created_issue | jq -r '.id') + + curl -X PUT -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "fields": { + "fixVersions": '"$parent_fix_versions"' + } + }' \ + "$JIRA_SERVER/rest/api/3/issue/$created_issue_id" +} + +extract_uuid() { + local url="$1" + local uuid_regex='[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}' + if [[ "$url" =~ $uuid_regex ]]; then + echo "${BASH_REMATCH}" + else + echo "No UUID found in the URL." + fi +} + +update_issue_status() { + local issue_key="$1" + local status_name="$2" + + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local transitions=$(curl -s -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/transitions") + + local transition_id=$(echo "$transitions" | jq -r --arg status_name "$status_name" ' + .transitions[] | select(.isAvailable == true and .to.name == $status_name) | .id + ') + + if [ -z "$transition_id" ]; then + echo "BadRequestError" + exit 1 + else + curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "transition": { + "id": "'"$transition_id"'" + } + }' \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/transitions" + fi +} + +add_attachments() { + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local issue_key="$1" + shift + local attachments=("$@") + + echo $issue_key + echo $attachments + + local form_data="" + for attachment in "${attachments[@]}"; do + form_data+="--form file=@$attachment " + done + + echo $form_data + + echo $(curl -X POST $form_data \ + -H "X-Atlassian-Token: no-check" \ + -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/attachments") +} + +add_comment() { + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + local issue_key="$1" + shift + local comment_items_input=("$@") + local comment_json="[]" + + for item in "${comment_items_input[@]}"; do + if [ -e "$item" ]; then + # If it's a file path + local attachment=$(add_attachments "$issue_key" "$item") + local id=$(echo $attachment | grep -oP '"id":\s*"\K[^"]+') + + local attachment_content=$(curl -s -I -L -H "Authorization: Basic $AUTH" "$JIRA_SERVER/rest/api/3/attachment/content/$id" \ + | grep -i "Location:" | tail -1 | awk '{print $2}' | tr -d '\r') + echo "$JIRA_SERVER/rest/api/3/attachment/content/$id" + echo "$attachment_content" + + local uuid=$(extract_uuid "$attachment_content") + echo "$uuid" + + json_object=$(jq -n --arg uuid "$uuid" '{ type: "mediaSingle", attrs: { layout: "align-start" }, content: [{ type: "media", attrs: { type: "file", id: $uuid, width: 200, height: 200, collection: "", alt: "" } }]}') + + comment_json=$(echo "$comment_json" | jq --argjson obj "$json_object" '. += [$obj]') + else + # If it's a string + json_object=$(jq -n --arg text "$item" '{ type: "paragraph", content: [{ type: "text", text: $text }]}') + + comment_json=$(echo "$comment_json" | jq --argjson obj "$json_object" '. += [$obj]') + fi + done + + request=$(jq -n --argjson content "$comment_json" '{body: { type: "doc", version: 1, content: $content }}') + + curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d "$request" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/comment" +} + +if [[ $# -gt 0 ]]; then # IF function call passed in + "$@" # Call function +fi \ No newline at end of file From 7d1ecb01b8b68d6b8e3492faf11721ce17e2feb1 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 16:45:48 -0500 Subject: [PATCH 18/40] Reverted dockerfile --- Dockerfile.playwright | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile.playwright b/Dockerfile.playwright index 6271a937..46a2114a 100644 --- a/Dockerfile.playwright +++ b/Dockerfile.playwright @@ -1,4 +1,4 @@ -FROM node:16 +FROM node:20 FROM mcr.microsoft.com/playwright:v1.48.0-noble @@ -12,10 +12,10 @@ COPY package*.json ./ RUN npm install # Install Playwright browsers -RUN npx playwright install --with-deps +RUN npx playwright install chromium --with-deps + +# Install jq +RUN apt-get install -y jq # Copy the rest of the application code COPY . . - -# Run Playwright tests -CMD ["npx", "playwright", "test"] From 7c301dff876709c3168910e06b18489db958e310 Mon Sep 17 00:00:00 2001 From: Chase King Date: Wed, 5 Mar 2025 16:57:16 -0500 Subject: [PATCH 19/40] Changed number of shards back to 4 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e4cb2666..c40e2c36 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,7 +36,7 @@ variables: - name: imageTag value: '$(Build.BuildId)' - name: totalShards - value: 50 #TODO: Change back before merge + value: 4 stages: # PR's From 6dba628d5abac5ef5bce48cd730e260796d83fef Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 14:20:35 -0500 Subject: [PATCH 20/40] Changes to run test suite by jira card number --- azure-pipelines-automated-testing.yml | 474 ++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 azure-pipelines-automated-testing.yml diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml new file mode 100644 index 00000000..ba2a2502 --- /dev/null +++ b/azure-pipelines-automated-testing.yml @@ -0,0 +1,474 @@ +trigger: + branches: + include: [ develop, release/* ] + paths: + exclude: + - deployment/* + include: + - "*" +pr: + branches: + include: [ '*' ] + paths: + exclude: + - deployment/* + include: + - "*" + +resources: + containers: + - container: node + image: packagerepository.sagaws.net:8082/safelite/node-build:18 + - container: awscli + image: packagerepository.sagaws.net:8082/safelite/awscli2-build:3.9 + repositories: + - repository: AzureDevOps + type: github + name: Safelite/AzureDevOps + endpoint: Safelite + ref: refs/tags/t5.5.40 + +variables: + - group: Digital-Infrastructure + - group: ISS-BuildBranches + - name: dockerImageName + value: 'playwright-tests' + - name: imageTag + value: '$(Build.BuildId)' + - name: totalShards + value: 4 + +stages: + # PR's + - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + - stage: TestPr + displayName: Run Tests For PullRequest + jobs: + - template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps + parameters: + nodeContainer: node + npmLocation: $(Build.SourcesDirectory) + testResultsFile: junit.xml + summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml + - job: playwright_tests + continueOnError: true + strategy: + matrix: + shard1: + shardNumber: 1 + shard2: + shardNumber: 2 + shard3: + shardNumber: 3 + shard4: + shardNumber: 4 + + steps: + - task: Docker@2 + displayName: 'Build Docker Image' + inputs: + command: build + dockerfile: Dockerfile.playwright + repository: $(dockerImageName) + tags: $(imageTag) + arguments: '--no-cache --pull' + + - script: | + branch_name=$(System.PullRequest.SourceBranch) + echo "Retrieved branch name: '$branch_name'" + jira_card_number="${branch_name##*/}" + echo "Extracted JIRA Card number: '$jira_card_number'" + # Create container and run tests + container_id=$(docker create \ + --ipc=host \ + -e CCIS_API_AUTH=$(CCIS_API_AUTH) \ + -e BASE_URL=$(BASE_URL) \ + -e CCIS_API_URL=$(CCIS_API_URL) \ + -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ + -e SHARD=$(shardNumber) \ + -e CI=true \ + -e NODE_ENV=$(NODE_ENV) \ + $(dockerImageName):$(imageTag) \ + npx concurrently -k -n "server,playwright"\ + "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ + "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$jira_card_number\"") + + # Start container and stream logs + echo "Starting tests for shard $(shardNumber)..." + docker start -a $container_id + + # Create directory for test results + echo "Creating test results directory..." + mkdir -p $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber) + + # Copy test results from container + echo "Copying test results..." + docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ + + # Remove container + echo "Cleaning up container..." + docker rm $container_id + + # Check if tests failed + if [ $? -ne 0 ]; then + echo "Tests failed in shard $(shardNumber)!" + exit 1 + fi + displayName: 'Run Playwright Tests - Shard $(shardNumber)' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Reports - Shard $(shardNumber)' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)' + artifact: 'playwright-report-shard-$(shardNumber)' + publishLocation: 'pipeline' + + - script: | + docker rmi $(dockerImageName):$(imageTag) -f + displayName: 'Cleanup Docker Image' + condition: always() + + - job: download_and_merge_reports + dependsOn: playwright_tests + timeoutInMinutes: 8 + cancelTimeoutInMinutes: 10 + steps: + - task: DownloadPipelineArtifact@2 + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports' + - task: Docker@2 + displayName: 'Build Docker Image' + inputs: + command: build + dockerfile: Dockerfile.playwright + repository: $(dockerImageName) + tags: $(imageTag) + arguments: '--no-cache --pull' + - bash: | + branch_name=$(System.PullRequest.SourceBranch) + echo "Retrieved branch name: '$branch_name'" + jira_card_number="${branch_name##*/}" + echo "Extracted JIRA Card number: '$jira_card_number'" + # Create container for jira writeback + container_id=$(docker create \ + --ipc=host \ + -e JIRA_SERVER=$(JIRA_SERVER) \ + -e JIRA_USERNAME=$(JIRA_USERNAME) \ + -e JIRA_API_KEY=$(JIRA_API_KEY) \ + -e jira_card_number="$jira_card_number" \ + $(dockerImageName):$(imageTag) \ + bash -c "echo \"Moving Playwright reports out of subfolders...\" && + find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && + echo \"Merging reports...\" && + npx playwright merge-reports --reporter=ortoni-report ./playwright-reports + echo \"Contents of ortoni-report:\" && ls ./ortoni-report && + echo 'Current dir: ' && pwd + echo 'Contents of current dir: ' && ls + echo 'Contents of /app/devops/scripts' && ls /app/devops/scripts + echo \"Writing report to Jira card '$jira_card_number'...\" && + /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + + # Start container and stream logs + echo "Starting merge" + docker start -a $container_id + + # Create directory for test results + echo "Creating test results directory..." + mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report + + # Copy test results from container + echo "Copying test results..." + docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report + # docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory) + # TODO: Update to include junit results if necessary + + # Remove container + echo "Cleaning up container..." + docker rm $container_id + env: + JIRA_API_KEY: $(JIRA_API_KEY) + displayName: merge_and_publish_results_to_jira + + # TODO: Enable later when working on PR gate + # - task: PublishTestResults@2 + # displayName: 'Publish test results' + # inputs: + # searchFolder: 'test-results' + # testResultsFormat: 'JUnit' + # testResultsFiles: 'junit_results.xml' + # mergeTestResults: true + # failTaskOnFailedTests: false + # testRunTitle: 'Playwright Tests' + # condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Merged Report' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' + artifact: 'playwright-merged-report' + publishLocation: 'pipeline' + - script: | + docker rmi $(dockerImageName):$(imageTag) -f + displayName: 'Cleanup Docker Image' + condition: always() + + - ${{ else }}: + # Dev Build/Deploy + - stage: Dev + condition: eq(variables['Build.SourceBranch'], variables['dev-branch'] ) + variables: + - group: ISS-Dev + jobs: + - deployment: devBuildDeployment + displayName: Build and Deploy ISS - Dev + environment: digitalCloud-dev + container: node + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - checkout: self + clean: true + - task: CopyFiles@2 + inputs: + SourceFolder: 'public/apple-pay/dev' + Contents: '**' + TargetFolder: 'public/.well-known' + CleanTargetFolder: true + - task: DeleteFiles@1 + inputs: + SourceFolder: 'public/apple-pay' + Contents: '**' + RemoveSourceFolder: true + - template: templates/digital/step-build-vue.yml@AzureDevOps + parameters: + buildOutputDir: dist + environment: Dev + - template: templates/digital/step-deploy-vue.yml@AzureDevOps + parameters: + artifactName: vueDistDev + awsProfile: $(devDeploymentProfile) + deployBuckets: + safelite-dev-iss-us-east-2: + clearFolder: true + deployFolder: '' + region: us-east-2 + safelite-dev-iss-us-east-1: + clearFolder: true + deployFolder: '' + region: us-east-1 + appDeployVariables: + __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) + __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) + __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) + __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) + __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) + indexDeployVariables: + __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) + __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) + __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) + - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps + parameters: + awsCliContainer: awscli + distributionId: $(cfDistributionId) + paths: /* + awsProfile: $(devDeploymentProfile) + + # Test Build/Deploy + - stage: Test + condition: eq(variables['Build.SourceBranch'], variables['test-branch'] ) + variables: + - group: ISS-Test + jobs: + - deployment: testBuildDeployment + displayName: Build and Deploy ISS - Test + environment: NoApproval-All + container: node + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - checkout: self + clean: true + - task: CopyFiles@2 + inputs: + SourceFolder: 'public/apple-pay/test' + Contents: '**' + TargetFolder: 'public/.well-known' + CleanTargetFolder: true + - task: DeleteFiles@1 + inputs: + SourceFolder: 'public/apple-pay' + Contents: '**' + RemoveSourceFolder: true + - template: templates/digital/step-build-vue.yml@AzureDevOps + parameters: + buildOutputDir: dist + environment: Test + - template: templates/digital/step-deploy-vue.yml@AzureDevOps + parameters: + artifactName: vueDistTest + awsProfile: $(sysDeploymentProfile) + deployBuckets: + safelite-sys-iss-us-east-1: + clearFolder: true + deployFolder: "" + region: us-east-1 + safelite-sys-iss-us-east-2: + clearFolder: true + deployFolder: "" + region: us-east-2 + appDeployVariables: + __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) + __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) + __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) + __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) + __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) + indexDeployVariables: + __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) + __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) + __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) + - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps + parameters: + awsCliContainer: awscli + distributionId: $(cfDistributionId) + paths: /* + awsProfile: $(sysDeploymentProfile) + + # QA Build/Deploy + - stage: QA + condition: eq(variables['Build.SourceBranch'], variables['qa-branch'] ) + variables: + - group: ISS-QA + jobs: + - deployment: qaBuildDeployment + displayName: Build and Deploy ISS - QA + environment: NoApproval-All + container: node + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - checkout: self + clean: true + - task: CopyFiles@2 + inputs: + SourceFolder: 'public/apple-pay/qa' + Contents: '**' + TargetFolder: 'public/.well-known' + CleanTargetFolder: true + - task: DeleteFiles@1 + inputs: + SourceFolder: 'public/apple-pay' + Contents: '**' + RemoveSourceFolder: true + - template: templates/digital/step-build-vue.yml@AzureDevOps + parameters: + buildOutputDir: dist + environment: Qa + - template: templates/digital/step-deploy-vue.yml@AzureDevOps + parameters: + artifactName: vueDistQa + awsProfile: $(qaDeploymentProfile) + deployBuckets: + safelite-qa-iss-us-east-1: + clearFolder: true + deployFolder: "" + region: us-east-1 + safelite-qa-iss-us-east-2: + clearFolder: true + deployFolder: "" + region: us-east-2 + appDeployVariables: + __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) + __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) + __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) + __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) + __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) + indexDeployVariables: + __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) + __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) + __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) + - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps + parameters: + awsCliContainer: awscli + distributionId: $(cfDistributionId) + paths: /* + awsProfile: $(qaDeploymentProfile) + + # Prod Build/Deploy + - stage: Prod + condition: succeeded('QA') + variables: + - group: ISS-Prod + jobs: + - deployment: prodBuildDeployment + displayName: Build and Deploy ISS - Prod + environment: digitalCloud-prod + container: node + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - checkout: self + clean: true + - task: CopyFiles@2 + inputs: + SourceFolder: 'public/apple-pay/prod' + Contents: '**' + TargetFolder: 'public/.well-known' + CleanTargetFolder: true + - task: DeleteFiles@1 + inputs: + SourceFolder: 'public/apple-pay' + Contents: '**' + RemoveSourceFolder: true + - template: templates/digital/step-build-vue.yml@AzureDevOps + parameters: + buildOutputDir: dist + environment: Prod + - template: templates/digital/step-deploy-vue.yml@AzureDevOps + parameters: + artifactName: vueDistProd + awsProfile: $(prodDeploymentProfile) + deployBuckets: + safelite-prod-iss-us-east-1: + clearFolder: true + deployFolder: "" + region: us-east-1 + safelite-prod-iss-us-east-2: + clearFolder: true + deployFolder: "" + region: us-east-2 + appDeployVariables: + __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) + __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) + __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) + __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) + __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) + indexDeployVariables: + __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) + __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) + __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) + - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps + parameters: + awsCliContainer: awscli + distributionId: $(cfDistributionId) + paths: /* + awsProfile: $(prodDeploymentProfile) + - template: templates/digital/auto-tag.yml@AzureDevOps + parameters: + dependsOn: prodBuildDeployment + userName: SafeliteAzureDevops + userEmail: githubazuredevops@safelite.com + From c16a4d823a1cf09d33a79a0101c06f6bb2635818 Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 14:23:16 -0500 Subject: [PATCH 21/40] Changes to test functionality --- azure-pipelines-automated-testing.yml | 6 +++--- playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index ba2a2502..20f6e479 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -40,7 +40,7 @@ variables: stages: # PR's - - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + - ${{ if or(eq(variables['Build.Reason'], 'PullRequest'), eq(1,1)) }}: # TODO: Change condition back to normal - stage: TestPr displayName: Run Tests For PullRequest jobs: @@ -74,7 +74,7 @@ stages: arguments: '--no-cache --pull' - script: | - branch_name=$(System.PullRequest.SourceBranch) + branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" jira_card_number="${branch_name##*/}" echo "Extracted JIRA Card number: '$jira_card_number'" @@ -146,7 +146,7 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - bash: | - branch_name=$(System.PullRequest.SourceBranch) + branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" jira_card_number="${branch_name##*/}" echo "Extracted JIRA Card number: '$jira_card_number'" diff --git a/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts index cc3da886..8e9c5b35 100644 --- a/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts +++ b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts @@ -61,7 +61,7 @@ for (const client of essentialClients) { data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false const tc = new TestCase({ name: `0001 Essential Replace Statis ADAS Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0001', '@test_report'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0001', '@test_report', '@INSR-2178'], //TODO: Remove jira card number testData: data }, undefined, '0001'); essentialReplaceStaticAdasTests.push(tc); From 26d421e224f03e445926027c4a2fb42f3acd32b2 Mon Sep 17 00:00:00 2001 From: chase-safelite Date: Thu, 6 Mar 2025 15:28:06 -0500 Subject: [PATCH 22/40] Jira Writeback Integration for Playwright tests (#927) * Updated to have timeout Jeremy suggested * Changed what variable is accessed * Temporarily changed number of shards for testing * Extract Jira Card Number from SourceBranchName * Changed the way we get source branch name * Escaped quotes * Added timeout for tasks that run even when canceled * Fixed unescaped quotes * Added logging * Changed the way we grab source branch name * Fixed pipeline * Moved grabbing branch name out of container * Fixed quote escaping * Attempting to grab source branch name * Added more logging and removed comments * Added more logging * Added jira writeback script * Reverted dockerfile * Changed number of shards back to 4 --- Dockerfile.playwright | 10 +-- azure-pipelines.yml | 96 ++++++++++++++------ devops/scripts/jira_writeback.sh | 149 +++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 32 deletions(-) create mode 100755 devops/scripts/jira_writeback.sh diff --git a/Dockerfile.playwright b/Dockerfile.playwright index 6271a937..46a2114a 100644 --- a/Dockerfile.playwright +++ b/Dockerfile.playwright @@ -1,4 +1,4 @@ -FROM node:16 +FROM node:20 FROM mcr.microsoft.com/playwright:v1.48.0-noble @@ -12,10 +12,10 @@ COPY package*.json ./ RUN npm install # Install Playwright browsers -RUN npx playwright install --with-deps +RUN npx playwright install chromium --with-deps + +# Install jq +RUN apt-get install -y jq # Copy the rest of the application code COPY . . - -# Run Playwright tests -CMD ["npx", "playwright", "test"] diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cedebb5b..c40e2c36 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -90,7 +90,6 @@ stages: "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@smoke | @Advanced\"") # Start container and stream logs - echo "Starting tests for shard $(shardNumber)..." docker start -a $container_id @@ -127,26 +126,65 @@ stages: condition: always() - job: download_and_merge_reports - container: - image: mcr.microsoft.com/playwright:v1.48.0-noble dependsOn: playwright_tests + timeoutInMinutes: 8 + cancelTimeoutInMinutes: 10 steps: - - task: DownloadPipelineArtifact@2 - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports' - - script: | - npm i ortoni-report && - for dir in $(System.DefaultWorkingDirectory)/playwright-reports/*/; do - if [ -d "$dir" ]; then - mv "$dir"* $(System.DefaultWorkingDirectory)/playwright-reports/ - rmdir "$dir" - fi - done - npx playwright merge-reports --reporter=ortoni-report,junit $(System.DefaultWorkingDirectory)/playwright-reports - ls - displayName: merge_reports - env: - PLAYWRIGHT_JUNIT_OUTPUT_FILE: "test-results/results.xml" + - task: DownloadPipelineArtifact@2 + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports' + - task: Docker@2 + displayName: 'Build Docker Image' + inputs: + command: build + dockerfile: Dockerfile.playwright + repository: $(dockerImageName) + tags: $(imageTag) + arguments: '--no-cache --pull' + - bash: | + branch_name=$(System.PullRequest.SourceBranch) + echo "Retrieved branch name: '$branch_name'" + jira_card_number="${branch_name##*/}" + echo "Extracted JIRA Card number: '$jira_card_number'" + # Create container for jira writeback + container_id=$(docker create \ + --ipc=host \ + -e JIRA_SERVER=$(JIRA_SERVER) \ + -e JIRA_USERNAME=$(JIRA_USERNAME) \ + -e JIRA_API_KEY=$(JIRA_API_KEY) \ + -e jira_card_number="$jira_card_number" \ + $(dockerImageName):$(imageTag) \ + bash -c "echo \"Moving Playwright reports out of subfolders...\" && + find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && + echo \"Merging reports...\" && + npx playwright merge-reports --reporter=ortoni-report ./playwright-reports + echo \"Contents of ortoni-report:\" && ls ./ortoni-report && + echo 'Current dir: ' && pwd + echo 'Contents of current dir: ' && ls + echo 'Contents of /app/devops/scripts' && ls /app/devops/scripts + echo \"Writing report to Jira card '$jira_card_number'...\" && + /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") + + # Start container and stream logs + echo "Starting merge" + docker start -a $container_id + + # Create directory for test results + echo "Creating test results directory..." + mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report + + # Copy test results from container + echo "Copying test results..." + docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report + # docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory) + # TODO: Update to include junit results if necessary + + # Remove container + echo "Cleaning up container..." + docker rm $container_id + env: + JIRA_API_KEY: $(JIRA_API_KEY) + displayName: merge_and_publish_results_to_jira # TODO: Enable later when working on PR gate # - task: PublishTestResults@2 @@ -154,19 +192,23 @@ stages: # inputs: # searchFolder: 'test-results' # testResultsFormat: 'JUnit' - # testResultsFiles: 'results.xml' + # testResultsFiles: 'junit_results.xml' # mergeTestResults: true # failTaskOnFailedTests: false # testRunTitle: 'Playwright Tests' # condition: succeededOrFailed() - - task: PublishPipelineArtifact@1 - displayName: 'Publish Merged Report' - condition: always() - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' - artifact: 'playwright-merged-report' - publishLocation: 'pipeline' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Merged Report' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' + artifact: 'playwright-merged-report' + publishLocation: 'pipeline' + - script: | + docker rmi $(dockerImageName):$(imageTag) -f + displayName: 'Cleanup Docker Image' + condition: always() - ${{ else }}: # Dev Build/Deploy diff --git a/devops/scripts/jira_writeback.sh b/devops/scripts/jira_writeback.sh new file mode 100755 index 00000000..1d605072 --- /dev/null +++ b/devops/scripts/jira_writeback.sh @@ -0,0 +1,149 @@ +#!/bin/bash +create_issue() { + local title="$1" + local project_key="$2" + local issue_type="$3" + local parent_issue_key="$4" + + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local parent_issue=$(curl -s -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$parent_issue_key") + + local parent_fix_versions=$(echo $parent_issue | jq -r '.fields.fixVersions') + + local created_issue=$(curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "fields": { + "summary": "'"$title"'", + "project": { + "key": "'"$project_key"'" + }, + "issuetype": { + "name": "'"$issue_type"'" + }, + "parent": { + "key": "'"$parent_issue_key"'" + }, + "fixVersions": '"$parent_fix_versions"' + } + }' \ + "$JIRA_SERVER/rest/api/3/issue") + + local created_issue_id=$(echo $created_issue | jq -r '.id') + + curl -X PUT -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "fields": { + "fixVersions": '"$parent_fix_versions"' + } + }' \ + "$JIRA_SERVER/rest/api/3/issue/$created_issue_id" +} + +extract_uuid() { + local url="$1" + local uuid_regex='[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}' + if [[ "$url" =~ $uuid_regex ]]; then + echo "${BASH_REMATCH}" + else + echo "No UUID found in the URL." + fi +} + +update_issue_status() { + local issue_key="$1" + local status_name="$2" + + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local transitions=$(curl -s -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/transitions") + + local transition_id=$(echo "$transitions" | jq -r --arg status_name "$status_name" ' + .transitions[] | select(.isAvailable == true and .to.name == $status_name) | .id + ') + + if [ -z "$transition_id" ]; then + echo "BadRequestError" + exit 1 + else + curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d '{ + "transition": { + "id": "'"$transition_id"'" + } + }' \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/transitions" + fi +} + +add_attachments() { + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + + local issue_key="$1" + shift + local attachments=("$@") + + echo $issue_key + echo $attachments + + local form_data="" + for attachment in "${attachments[@]}"; do + form_data+="--form file=@$attachment " + done + + echo $form_data + + echo $(curl -X POST $form_data \ + -H "X-Atlassian-Token: no-check" \ + -H "Authorization: Basic $AUTH" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/attachments") +} + +add_comment() { + AUTH=$(echo -ne "$JIRA_USERNAME:$JIRA_API_KEY" | base64 --wrap 0) + local issue_key="$1" + shift + local comment_items_input=("$@") + local comment_json="[]" + + for item in "${comment_items_input[@]}"; do + if [ -e "$item" ]; then + # If it's a file path + local attachment=$(add_attachments "$issue_key" "$item") + local id=$(echo $attachment | grep -oP '"id":\s*"\K[^"]+') + + local attachment_content=$(curl -s -I -L -H "Authorization: Basic $AUTH" "$JIRA_SERVER/rest/api/3/attachment/content/$id" \ + | grep -i "Location:" | tail -1 | awk '{print $2}' | tr -d '\r') + echo "$JIRA_SERVER/rest/api/3/attachment/content/$id" + echo "$attachment_content" + + local uuid=$(extract_uuid "$attachment_content") + echo "$uuid" + + json_object=$(jq -n --arg uuid "$uuid" '{ type: "mediaSingle", attrs: { layout: "align-start" }, content: [{ type: "media", attrs: { type: "file", id: $uuid, width: 200, height: 200, collection: "", alt: "" } }]}') + + comment_json=$(echo "$comment_json" | jq --argjson obj "$json_object" '. += [$obj]') + else + # If it's a string + json_object=$(jq -n --arg text "$item" '{ type: "paragraph", content: [{ type: "text", text: $text }]}') + + comment_json=$(echo "$comment_json" | jq --argjson obj "$json_object" '. += [$obj]') + fi + done + + request=$(jq -n --argjson content "$comment_json" '{body: { type: "doc", version: 1, content: $content }}') + + curl -X POST -H "Content-Type: application/json" \ + -H "Authorization: Basic $AUTH" \ + -d "$request" \ + "$JIRA_SERVER/rest/api/3/issue/$issue_key/comment" +} + +if [[ $# -gt 0 ]]; then # IF function call passed in + "$@" # Call function +fi \ No newline at end of file From 2ea0a61529a50d26149c0289e84f93352c37767e Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 15:59:43 -0500 Subject: [PATCH 23/40] Added publish Junit test results --- azure-pipelines-automated-testing.yml | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 20f6e479..7a96e1bc 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -160,8 +160,10 @@ stages: $(dockerImageName):$(imageTag) \ bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && + PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test_results' + PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' echo \"Merging reports...\" && - npx playwright merge-reports --reporter=ortoni-report ./playwright-reports + npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports echo \"Contents of ortoni-report:\" && ls ./ortoni-report && echo 'Current dir: ' && pwd echo 'Contents of current dir: ' && ls @@ -180,8 +182,7 @@ stages: # Copy test results from container echo "Copying test results..." docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report - # docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory) - # TODO: Update to include junit results if necessary + docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory)/test-results # Remove container echo "Cleaning up container..." @@ -190,17 +191,16 @@ stages: JIRA_API_KEY: $(JIRA_API_KEY) displayName: merge_and_publish_results_to_jira - # TODO: Enable later when working on PR gate - # - task: PublishTestResults@2 - # displayName: 'Publish test results' - # inputs: - # searchFolder: 'test-results' - # testResultsFormat: 'JUnit' - # testResultsFiles: 'junit_results.xml' - # mergeTestResults: true - # failTaskOnFailedTests: false - # testRunTitle: 'Playwright Tests' - # condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: 'Publish test results' + inputs: + searchFolder: 'test-results' + testResultsFormat: 'JUnit' + testResultsFiles: 'junit_results.xml' + mergeTestResults: true + failTaskOnFailedTests: false + testRunTitle: 'Playwright Tests' + condition: succeededOrFailed() - task: PublishPipelineArtifact@1 displayName: 'Publish Merged Report' From 50c6d8e5baf748cd18d9aa0d5023002efa05cb5a Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 15:59:59 -0500 Subject: [PATCH 24/40] Changed selected tests to advanced --- playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts | 2 +- playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts | 2 +- playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts | 2 +- playwright-tests/tests/advanced/0003a_MobileAfterpay.ts | 2 +- playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts index 8e9c5b35..cc3da886 100644 --- a/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts +++ b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts @@ -61,7 +61,7 @@ for (const client of essentialClients) { data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false const tc = new TestCase({ name: `0001 Essential Replace Statis ADAS Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0001', '@test_report', '@INSR-2178'], //TODO: Remove jira card number + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0001', '@test_report'], testData: data }, undefined, '0001'); essentialReplaceStaticAdasTests.push(tc); diff --git a/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts index dc64aa11..467b7223 100644 --- a/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts +++ b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts @@ -71,7 +71,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0001a_Advanced_Replace_Deductible_Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], testData: data }, undefined, '0001a'); advancedScenario0001TestCases.push(tc); diff --git a/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts index 2d6bf104..cff67a41 100644 --- a/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts +++ b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts @@ -119,7 +119,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0002a_Advanced_Replace_Deductible_Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], testData: data }, undefined, '0002a'); advancedScenario0002TestCases.push(tc); diff --git a/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts index 6e494225..836bce09 100644 --- a/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts +++ b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts @@ -86,7 +86,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0003a Advanced Replace Deductible Mobile Afterpay Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], testData: data }, undefined, '0003a'); advancedScenario0003TestCases.push(tc); diff --git a/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts index b64aaac5..f7b7b1bc 100644 --- a/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts +++ b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts @@ -74,7 +74,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0004a Advanced Replace Deductible Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], testData: data }, undefined, '0004a'); advancedScenario0004aTestCases.push(tc); From 4fe9570dfb4c4d56cba221d152ca5c4a254e71f9 Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 16:00:53 -0500 Subject: [PATCH 25/40] Fixed spacing --- azure-pipelines-automated-testing.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 7a96e1bc..8fdf45c8 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -191,16 +191,16 @@ stages: JIRA_API_KEY: $(JIRA_API_KEY) displayName: merge_and_publish_results_to_jira - - task: PublishTestResults@2 - displayName: 'Publish test results' - inputs: - searchFolder: 'test-results' - testResultsFormat: 'JUnit' - testResultsFiles: 'junit_results.xml' - mergeTestResults: true - failTaskOnFailedTests: false - testRunTitle: 'Playwright Tests' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: 'Publish test results' + inputs: + searchFolder: 'test-results' + testResultsFormat: 'JUnit' + testResultsFiles: 'junit_results.xml' + mergeTestResults: true + failTaskOnFailedTests: false + testRunTitle: 'Playwright Tests' + condition: succeededOrFailed() - task: PublishPipelineArtifact@1 displayName: 'Publish Merged Report' From 5ef75ad1485a84c19a23399d3761ef55b9fef4aa Mon Sep 17 00:00:00 2001 From: Chase King Date: Thu, 6 Mar 2025 16:20:36 -0500 Subject: [PATCH 26/40] JUnit reporting attempt #2 --- azure-pipelines-automated-testing.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 8fdf45c8..0a95fa35 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -160,10 +160,8 @@ stages: $(dockerImageName):$(imageTag) \ bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && - PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test_results' - PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' echo \"Merging reports...\" && - npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports + PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test_results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports echo \"Contents of ortoni-report:\" && ls ./ortoni-report && echo 'Current dir: ' && pwd echo 'Contents of current dir: ' && ls From af63c7a2975255f381eff9ea49ed1e93c48f9b61 Mon Sep 17 00:00:00 2001 From: Chase King Date: Fri, 7 Mar 2025 08:55:01 -0500 Subject: [PATCH 27/40] Fixed test-results path --- azure-pipelines-automated-testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 0a95fa35..44f71cc5 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -161,11 +161,11 @@ stages: bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && echo \"Merging reports...\" && - PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test_results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports + PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test-results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports echo \"Contents of ortoni-report:\" && ls ./ortoni-report && echo 'Current dir: ' && pwd echo 'Contents of current dir: ' && ls - echo 'Contents of /app/devops/scripts' && ls /app/devops/scripts + echo 'Contents of /app/test-results' && ls /app/test-results echo \"Writing report to Jira card '$jira_card_number'...\" && /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") From ed4e2ea33c677faea1dacb6fc401f3f6dc504298 Mon Sep 17 00:00:00 2001 From: Chase King Date: Fri, 7 Mar 2025 10:09:12 -0500 Subject: [PATCH 28/40] Created dir for test results --- azure-pipelines-automated-testing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 44f71cc5..3a34e8be 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -176,6 +176,7 @@ stages: # Create directory for test results echo "Creating test results directory..." mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report + mkdir -p $(System.DefaultWorkingDirectory)/test-results # Copy test results from container echo "Copying test results..." From da4e992985c8427d0d40978017693c68f2b490e4 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 10:04:48 -0400 Subject: [PATCH 29/40] Added tolerance for test num < 4 --- azure-pipelines-automated-testing.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 3a34e8be..2bbd6868 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -73,7 +73,7 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - - script: | + - bash: | branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" jira_card_number="${branch_name##*/}" @@ -89,9 +89,17 @@ stages: -e CI=true \ -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ - npx concurrently -k -n "server,playwright"\ - "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ - "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$jira_card_number\"") + bash -c "num_tests=$(npx playwright test --list --grep \"@$jira_card_number\") + num_tests=${num_tests#*Total: } + num_tests=${num_tests%% test*} + if [[ $num_tests -lt $(shardNumber) ]]; then + echo 'There are fewer tests than shards. Skipping this shard.' + else + npx concurrently -k -n \"server,playwright\" + \"sed -i \\\"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\\\" \\\"./vue.config.js\\\" && echo \\\"Updated config file to use TEST APIs\\\" && npm run serve -- --port=8080\" + \"npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \\\"@$jira_card_number\\\"\" + fi + ") # Start container and stream logs echo "Starting tests for shard $(shardNumber)..." @@ -103,7 +111,11 @@ stages: # Copy test results from container echo "Copying test results..." - docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ + if docker exec "$container_id" test -e "/app/blob-report"; then + docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ + else + echo "Test results file not found." + fi # Remove container echo "Cleaning up container..." From 35f216207c9f480b490f3102425645881782b8ed Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 10:05:09 -0400 Subject: [PATCH 30/40] Made it so there are fewer than 4 tests --- playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts index f7b7b1bc..b64aaac5 100644 --- a/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts +++ b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts @@ -74,7 +74,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0004a Advanced Replace Deductible Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], testData: data }, undefined, '0004a'); advancedScenario0004aTestCases.push(tc); From 1fa22cceeff3d0744bdfc4b9e5ec89e2cd8c3e7f Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 10:23:49 -0400 Subject: [PATCH 31/40] Fixed calculation of number of tests --- azure-pipelines-automated-testing.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 2bbd6868..5c0725aa 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -89,9 +89,11 @@ stages: -e CI=true \ -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ - bash -c "num_tests=$(npx playwright test --list --grep \"@$jira_card_number\") + bash -c "num_tests=$(npx playwright test playwright-tests --list --grep \"@$jira_card_number\") + echo $num_tests num_tests=${num_tests#*Total: } num_tests=${num_tests%% test*} + echo \"Extracted number of tests: $num_tests\" if [[ $num_tests -lt $(shardNumber) ]]; then echo 'There are fewer tests than shards. Skipping this shard.' else From 875fc2eaca62b9171e7b9e155fe785a62e8ad390 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 10:29:46 -0400 Subject: [PATCH 32/40] Attempt #2 --- azure-pipelines-automated-testing.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 5c0725aa..3fa6d862 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -87,7 +87,6 @@ stages: -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ -e SHARD=$(shardNumber) \ -e CI=true \ - -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ bash -c "num_tests=$(npx playwright test playwright-tests --list --grep \"@$jira_card_number\") echo $num_tests @@ -101,7 +100,7 @@ stages: \"sed -i \\\"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\\\" \\\"./vue.config.js\\\" && echo \\\"Updated config file to use TEST APIs\\\" && npm run serve -- --port=8080\" \"npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \\\"@$jira_card_number\\\"\" fi - ") + ") # Start container and stream logs echo "Starting tests for shard $(shardNumber)..." From 5f0da0eb86328f782c4ba3ded95173878a137602 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 10:38:04 -0400 Subject: [PATCH 33/40] attempt #3 --- azure-pipelines-automated-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 3fa6d862..f36128d1 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -73,7 +73,7 @@ stages: tags: $(imageTag) arguments: '--no-cache --pull' - - bash: | + - script: | branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) echo "Retrieved branch name: '$branch_name'" jira_card_number="${branch_name##*/}" From f6de29e9fd5289912e0915dcce746582cc4fd5c3 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 13:46:31 -0400 Subject: [PATCH 34/40] New strategy --- azure-pipelines-automated-testing.yml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index f36128d1..ffc143f8 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -87,20 +87,11 @@ stages: -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ -e SHARD=$(shardNumber) \ -e CI=true \ + -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ - bash -c "num_tests=$(npx playwright test playwright-tests --list --grep \"@$jira_card_number\") - echo $num_tests - num_tests=${num_tests#*Total: } - num_tests=${num_tests%% test*} - echo \"Extracted number of tests: $num_tests\" - if [[ $num_tests -lt $(shardNumber) ]]; then - echo 'There are fewer tests than shards. Skipping this shard.' - else - npx concurrently -k -n \"server,playwright\" - \"sed -i \\\"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\\\" \\\"./vue.config.js\\\" && echo \\\"Updated config file to use TEST APIs\\\" && npm run serve -- --port=8080\" - \"npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \\\"@$jira_card_number\\\"\" - fi - ") + npx concurrently -k -n "server,playwright"\ + "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ + "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$jira_card_number\"") # Start container and stream logs echo "Starting tests for shard $(shardNumber)..." From 61750f5f00074208f1a572ec4c72366fefdce091 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:19:43 -0400 Subject: [PATCH 35/40] Removed docker exec --- azure-pipelines-automated-testing.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index ffc143f8..056a0fb9 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -103,11 +103,7 @@ stages: # Copy test results from container echo "Copying test results..." - if docker exec "$container_id" test -e "/app/blob-report"; then - docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ - else - echo "Test results file not found." - fi + docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ # Remove container echo "Cleaning up container..." @@ -115,8 +111,8 @@ stages: # Check if tests failed if [ $? -ne 0 ]; then - echo "Tests failed in shard $(shardNumber)!" - exit 1 + echo "Tests failed in shard $(shardNumber) or tests don't exist for this shard number!" + exit 0 # Suppress error. It will be visible in report. fi displayName: 'Run Playwright Tests - Shard $(shardNumber)' From bb1df18dac3e34b6173f523c2ed682c3f3a6a219 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:21:34 -0400 Subject: [PATCH 36/40] Reduced shards to 2 --- azure-pipelines-automated-testing.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 056a0fb9..50cef605 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -36,7 +36,7 @@ variables: - name: imageTag value: '$(Build.BuildId)' - name: totalShards - value: 4 + value: 2 stages: # PR's @@ -58,10 +58,10 @@ stages: shardNumber: 1 shard2: shardNumber: 2 - shard3: - shardNumber: 3 - shard4: - shardNumber: 4 + # shard3: + # shardNumber: 3 + # shard4: + # shardNumber: 4 steps: - task: Docker@2 From c82ee470f8cb2f4b1ee4755a10c4c08d35a846a4 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:22:27 -0400 Subject: [PATCH 37/40] Removed tags from test cases --- playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts | 2 +- playwright-tests/tests/advanced/0003a_MobileAfterpay.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts index cff67a41..2d6bf104 100644 --- a/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts +++ b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts @@ -119,7 +119,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0002a_Advanced_Replace_Deductible_Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], testData: data }, undefined, '0002a'); advancedScenario0002TestCases.push(tc); diff --git a/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts index 836bce09..6e494225 100644 --- a/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts +++ b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts @@ -86,7 +86,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0003a Advanced Replace Deductible Mobile Afterpay Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], testData: data }, undefined, '0003a'); advancedScenario0003TestCases.push(tc); From 41726882052973c6b922bb63edd311c5d87c3102 Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:24:09 -0400 Subject: [PATCH 38/40] Commented out unit tests temporarily --- azure-pipelines-automated-testing.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml index 50cef605..6ed3654b 100644 --- a/azure-pipelines-automated-testing.yml +++ b/azure-pipelines-automated-testing.yml @@ -44,12 +44,13 @@ stages: - stage: TestPr displayName: Run Tests For PullRequest jobs: - - template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps - parameters: - nodeContainer: node - npmLocation: $(Build.SourcesDirectory) - testResultsFile: junit.xml - summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml + # TODO: Put unit tests back + # - template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps + # parameters: + # nodeContainer: node + # npmLocation: $(Build.SourcesDirectory) + # testResultsFile: junit.xml + # summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml - job: playwright_tests continueOnError: true strategy: From e29d5d0d7bdb74508de9a0e9f5b834f13f5f7faf Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:41:00 -0400 Subject: [PATCH 39/40] Moved changes to main pipeline --- azure-pipelines-automated-testing.yml | 474 -------------------------- azure-pipelines.yml | 49 +-- 2 files changed, 26 insertions(+), 497 deletions(-) delete mode 100644 azure-pipelines-automated-testing.yml diff --git a/azure-pipelines-automated-testing.yml b/azure-pipelines-automated-testing.yml deleted file mode 100644 index 6ed3654b..00000000 --- a/azure-pipelines-automated-testing.yml +++ /dev/null @@ -1,474 +0,0 @@ -trigger: - branches: - include: [ develop, release/* ] - paths: - exclude: - - deployment/* - include: - - "*" -pr: - branches: - include: [ '*' ] - paths: - exclude: - - deployment/* - include: - - "*" - -resources: - containers: - - container: node - image: packagerepository.sagaws.net:8082/safelite/node-build:18 - - container: awscli - image: packagerepository.sagaws.net:8082/safelite/awscli2-build:3.9 - repositories: - - repository: AzureDevOps - type: github - name: Safelite/AzureDevOps - endpoint: Safelite - ref: refs/tags/t5.5.40 - -variables: - - group: Digital-Infrastructure - - group: ISS-BuildBranches - - name: dockerImageName - value: 'playwright-tests' - - name: imageTag - value: '$(Build.BuildId)' - - name: totalShards - value: 2 - -stages: - # PR's - - ${{ if or(eq(variables['Build.Reason'], 'PullRequest'), eq(1,1)) }}: # TODO: Change condition back to normal - - stage: TestPr - displayName: Run Tests For PullRequest - jobs: - # TODO: Put unit tests back - # - template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps - # parameters: - # nodeContainer: node - # npmLocation: $(Build.SourcesDirectory) - # testResultsFile: junit.xml - # summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml - - job: playwright_tests - continueOnError: true - strategy: - matrix: - shard1: - shardNumber: 1 - shard2: - shardNumber: 2 - # shard3: - # shardNumber: 3 - # shard4: - # shardNumber: 4 - - steps: - - task: Docker@2 - displayName: 'Build Docker Image' - inputs: - command: build - dockerfile: Dockerfile.playwright - repository: $(dockerImageName) - tags: $(imageTag) - arguments: '--no-cache --pull' - - - script: | - branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) - echo "Retrieved branch name: '$branch_name'" - jira_card_number="${branch_name##*/}" - echo "Extracted JIRA Card number: '$jira_card_number'" - # Create container and run tests - container_id=$(docker create \ - --ipc=host \ - -e CCIS_API_AUTH=$(CCIS_API_AUTH) \ - -e BASE_URL=$(BASE_URL) \ - -e CCIS_API_URL=$(CCIS_API_URL) \ - -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ - -e SHARD=$(shardNumber) \ - -e CI=true \ - -e NODE_ENV=$(NODE_ENV) \ - $(dockerImageName):$(imageTag) \ - npx concurrently -k -n "server,playwright"\ - "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ - "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$jira_card_number\"") - - # Start container and stream logs - echo "Starting tests for shard $(shardNumber)..." - docker start -a $container_id - - # Create directory for test results - echo "Creating test results directory..." - mkdir -p $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber) - - # Copy test results from container - echo "Copying test results..." - docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ - - # Remove container - echo "Cleaning up container..." - docker rm $container_id - - # Check if tests failed - if [ $? -ne 0 ]; then - echo "Tests failed in shard $(shardNumber) or tests don't exist for this shard number!" - exit 0 # Suppress error. It will be visible in report. - fi - displayName: 'Run Playwright Tests - Shard $(shardNumber)' - - - task: PublishPipelineArtifact@1 - displayName: 'Publish Test Reports - Shard $(shardNumber)' - condition: always() - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)' - artifact: 'playwright-report-shard-$(shardNumber)' - publishLocation: 'pipeline' - - - script: | - docker rmi $(dockerImageName):$(imageTag) -f - displayName: 'Cleanup Docker Image' - condition: always() - - - job: download_and_merge_reports - dependsOn: playwright_tests - timeoutInMinutes: 8 - cancelTimeoutInMinutes: 10 - steps: - - task: DownloadPipelineArtifact@2 - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports' - - task: Docker@2 - displayName: 'Build Docker Image' - inputs: - command: build - dockerfile: Dockerfile.playwright - repository: $(dockerImageName) - tags: $(imageTag) - arguments: '--no-cache --pull' - - bash: | - branch_name=$(Build.SourceBranch) #TODO: Change back to $(System.PullRequest.SourceBranch) - echo "Retrieved branch name: '$branch_name'" - jira_card_number="${branch_name##*/}" - echo "Extracted JIRA Card number: '$jira_card_number'" - # Create container for jira writeback - container_id=$(docker create \ - --ipc=host \ - -e JIRA_SERVER=$(JIRA_SERVER) \ - -e JIRA_USERNAME=$(JIRA_USERNAME) \ - -e JIRA_API_KEY=$(JIRA_API_KEY) \ - -e jira_card_number="$jira_card_number" \ - $(dockerImageName):$(imageTag) \ - bash -c "echo \"Moving Playwright reports out of subfolders...\" && - find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && - echo \"Merging reports...\" && - PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test-results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports - echo \"Contents of ortoni-report:\" && ls ./ortoni-report && - echo 'Current dir: ' && pwd - echo 'Contents of current dir: ' && ls - echo 'Contents of /app/test-results' && ls /app/test-results - echo \"Writing report to Jira card '$jira_card_number'...\" && - /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") - - # Start container and stream logs - echo "Starting merge" - docker start -a $container_id - - # Create directory for test results - echo "Creating test results directory..." - mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report - mkdir -p $(System.DefaultWorkingDirectory)/test-results - - # Copy test results from container - echo "Copying test results..." - docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report - docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory)/test-results - - # Remove container - echo "Cleaning up container..." - docker rm $container_id - env: - JIRA_API_KEY: $(JIRA_API_KEY) - displayName: merge_and_publish_results_to_jira - - - task: PublishTestResults@2 - displayName: 'Publish test results' - inputs: - searchFolder: 'test-results' - testResultsFormat: 'JUnit' - testResultsFiles: 'junit_results.xml' - mergeTestResults: true - failTaskOnFailedTests: false - testRunTitle: 'Playwright Tests' - condition: succeededOrFailed() - - - task: PublishPipelineArtifact@1 - displayName: 'Publish Merged Report' - condition: always() - inputs: - targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' - artifact: 'playwright-merged-report' - publishLocation: 'pipeline' - - script: | - docker rmi $(dockerImageName):$(imageTag) -f - displayName: 'Cleanup Docker Image' - condition: always() - - - ${{ else }}: - # Dev Build/Deploy - - stage: Dev - condition: eq(variables['Build.SourceBranch'], variables['dev-branch'] ) - variables: - - group: ISS-Dev - jobs: - - deployment: devBuildDeployment - displayName: Build and Deploy ISS - Dev - environment: digitalCloud-dev - container: node - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - checkout: self - clean: true - - task: CopyFiles@2 - inputs: - SourceFolder: 'public/apple-pay/dev' - Contents: '**' - TargetFolder: 'public/.well-known' - CleanTargetFolder: true - - task: DeleteFiles@1 - inputs: - SourceFolder: 'public/apple-pay' - Contents: '**' - RemoveSourceFolder: true - - template: templates/digital/step-build-vue.yml@AzureDevOps - parameters: - buildOutputDir: dist - environment: Dev - - template: templates/digital/step-deploy-vue.yml@AzureDevOps - parameters: - artifactName: vueDistDev - awsProfile: $(devDeploymentProfile) - deployBuckets: - safelite-dev-iss-us-east-2: - clearFolder: true - deployFolder: '' - region: us-east-2 - safelite-dev-iss-us-east-1: - clearFolder: true - deployFolder: '' - region: us-east-1 - appDeployVariables: - __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) - __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) - __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) - __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) - __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) - indexDeployVariables: - __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) - __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) - __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) - - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps - parameters: - awsCliContainer: awscli - distributionId: $(cfDistributionId) - paths: /* - awsProfile: $(devDeploymentProfile) - - # Test Build/Deploy - - stage: Test - condition: eq(variables['Build.SourceBranch'], variables['test-branch'] ) - variables: - - group: ISS-Test - jobs: - - deployment: testBuildDeployment - displayName: Build and Deploy ISS - Test - environment: NoApproval-All - container: node - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - checkout: self - clean: true - - task: CopyFiles@2 - inputs: - SourceFolder: 'public/apple-pay/test' - Contents: '**' - TargetFolder: 'public/.well-known' - CleanTargetFolder: true - - task: DeleteFiles@1 - inputs: - SourceFolder: 'public/apple-pay' - Contents: '**' - RemoveSourceFolder: true - - template: templates/digital/step-build-vue.yml@AzureDevOps - parameters: - buildOutputDir: dist - environment: Test - - template: templates/digital/step-deploy-vue.yml@AzureDevOps - parameters: - artifactName: vueDistTest - awsProfile: $(sysDeploymentProfile) - deployBuckets: - safelite-sys-iss-us-east-1: - clearFolder: true - deployFolder: "" - region: us-east-1 - safelite-sys-iss-us-east-2: - clearFolder: true - deployFolder: "" - region: us-east-2 - appDeployVariables: - __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) - __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) - __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) - __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) - __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) - indexDeployVariables: - __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) - __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) - __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) - - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps - parameters: - awsCliContainer: awscli - distributionId: $(cfDistributionId) - paths: /* - awsProfile: $(sysDeploymentProfile) - - # QA Build/Deploy - - stage: QA - condition: eq(variables['Build.SourceBranch'], variables['qa-branch'] ) - variables: - - group: ISS-QA - jobs: - - deployment: qaBuildDeployment - displayName: Build and Deploy ISS - QA - environment: NoApproval-All - container: node - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - checkout: self - clean: true - - task: CopyFiles@2 - inputs: - SourceFolder: 'public/apple-pay/qa' - Contents: '**' - TargetFolder: 'public/.well-known' - CleanTargetFolder: true - - task: DeleteFiles@1 - inputs: - SourceFolder: 'public/apple-pay' - Contents: '**' - RemoveSourceFolder: true - - template: templates/digital/step-build-vue.yml@AzureDevOps - parameters: - buildOutputDir: dist - environment: Qa - - template: templates/digital/step-deploy-vue.yml@AzureDevOps - parameters: - artifactName: vueDistQa - awsProfile: $(qaDeploymentProfile) - deployBuckets: - safelite-qa-iss-us-east-1: - clearFolder: true - deployFolder: "" - region: us-east-1 - safelite-qa-iss-us-east-2: - clearFolder: true - deployFolder: "" - region: us-east-2 - appDeployVariables: - __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) - __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) - __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) - __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) - __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) - indexDeployVariables: - __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) - __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) - __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) - - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps - parameters: - awsCliContainer: awscli - distributionId: $(cfDistributionId) - paths: /* - awsProfile: $(qaDeploymentProfile) - - # Prod Build/Deploy - - stage: Prod - condition: succeeded('QA') - variables: - - group: ISS-Prod - jobs: - - deployment: prodBuildDeployment - displayName: Build and Deploy ISS - Prod - environment: digitalCloud-prod - container: node - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - checkout: self - clean: true - - task: CopyFiles@2 - inputs: - SourceFolder: 'public/apple-pay/prod' - Contents: '**' - TargetFolder: 'public/.well-known' - CleanTargetFolder: true - - task: DeleteFiles@1 - inputs: - SourceFolder: 'public/apple-pay' - Contents: '**' - RemoveSourceFolder: true - - template: templates/digital/step-build-vue.yml@AzureDevOps - parameters: - buildOutputDir: dist - environment: Prod - - template: templates/digital/step-deploy-vue.yml@AzureDevOps - parameters: - artifactName: vueDistProd - awsProfile: $(prodDeploymentProfile) - deployBuckets: - safelite-prod-iss-us-east-1: - clearFolder: true - deployFolder: "" - region: us-east-1 - safelite-prod-iss-us-east-2: - clearFolder: true - deployFolder: "" - region: us-east-2 - appDeployVariables: - __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) - __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) - __VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__) - __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) - __VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__) - indexDeployVariables: - __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) - __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) - __VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__) - - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps - parameters: - awsCliContainer: awscli - distributionId: $(cfDistributionId) - paths: /* - awsProfile: $(prodDeploymentProfile) - - template: templates/digital/auto-tag.yml@AzureDevOps - parameters: - dependsOn: prodBuildDeployment - userName: SafeliteAzureDevops - userEmail: githubazuredevops@safelite.com - diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c40e2c36..3d648d3c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,7 +36,7 @@ variables: - name: imageTag value: '$(Build.BuildId)' - name: totalShards - value: 4 + value: 2 stages: # PR's @@ -58,10 +58,10 @@ stages: shardNumber: 1 shard2: shardNumber: 2 - shard3: - shardNumber: 3 - shard4: - shardNumber: 4 + # shard3: + # shardNumber: 3 + # shard4: + # shardNumber: 4 steps: - task: Docker@2 @@ -74,6 +74,10 @@ stages: arguments: '--no-cache --pull' - script: | + branch_name=$(System.PullRequest.SourceBranch) + echo "Retrieved branch name: '$branch_name'" + jira_card_number="${branch_name##*/}" + echo "Extracted JIRA Card number: '$jira_card_number'" # Create container and run tests container_id=$(docker create \ --ipc=host \ @@ -87,7 +91,7 @@ stages: $(dockerImageName):$(imageTag) \ npx concurrently -k -n "server,playwright"\ "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ - "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@smoke | @Advanced\"") + "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$jira_card_number\"") # Start container and stream logs echo "Starting tests for shard $(shardNumber)..." @@ -107,8 +111,8 @@ stages: # Check if tests failed if [ $? -ne 0 ]; then - echo "Tests failed in shard $(shardNumber)!" - exit 1 + echo "Tests failed in shard $(shardNumber) or tests don't exist for this shard number!" + exit 0 # Suppress error. It will be visible in report. fi displayName: 'Run Playwright Tests - Shard $(shardNumber)' @@ -157,11 +161,11 @@ stages: bash -c "echo \"Moving Playwright reports out of subfolders...\" && find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; && echo \"Merging reports...\" && - npx playwright merge-reports --reporter=ortoni-report ./playwright-reports + PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test-results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=ortoni-report,junit ./playwright-reports echo \"Contents of ortoni-report:\" && ls ./ortoni-report && echo 'Current dir: ' && pwd echo 'Contents of current dir: ' && ls - echo 'Contents of /app/devops/scripts' && ls /app/devops/scripts + echo 'Contents of /app/test-results' && ls /app/test-results echo \"Writing report to Jira card '$jira_card_number'...\" && /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/ortoni-report.html \"AUTOMATED TEST RUN: $(date)\" ") @@ -172,12 +176,12 @@ stages: # Create directory for test results echo "Creating test results directory..." mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report + mkdir -p $(System.DefaultWorkingDirectory)/test-results # Copy test results from container echo "Copying test results..." docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report - # docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory) - # TODO: Update to include junit results if necessary + docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory)/test-results # Remove container echo "Cleaning up container..." @@ -186,17 +190,16 @@ stages: JIRA_API_KEY: $(JIRA_API_KEY) displayName: merge_and_publish_results_to_jira - # TODO: Enable later when working on PR gate - # - task: PublishTestResults@2 - # displayName: 'Publish test results' - # inputs: - # searchFolder: 'test-results' - # testResultsFormat: 'JUnit' - # testResultsFiles: 'junit_results.xml' - # mergeTestResults: true - # failTaskOnFailedTests: false - # testRunTitle: 'Playwright Tests' - # condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: 'Publish test results' + inputs: + searchFolder: 'test-results' + testResultsFormat: 'JUnit' + testResultsFiles: 'junit_results.xml' + mergeTestResults: true + failTaskOnFailedTests: false + testRunTitle: 'Playwright Tests' + condition: succeededOrFailed() - task: PublishPipelineArtifact@1 displayName: 'Publish Merged Report' From f925d542247b83bd0c85040b78488c60df6d22aa Mon Sep 17 00:00:00 2001 From: Chase King Date: Tue, 11 Mar 2025 14:42:05 -0400 Subject: [PATCH 40/40] Removed unnecessary jira tag --- playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts index 467b7223..dc64aa11 100644 --- a/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts +++ b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts @@ -71,7 +71,7 @@ for (const client of advancedClients) { data.clientTag = client.clientTag; const tc = new TestCase({ name: `0001a_Advanced_Replace_Deductible_Client: "${client.accountName}"`, - tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@INSR-2178'], + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], testData: data }, undefined, '0001a'); advancedScenario0001TestCases.push(tc);