Update files for Playwright pipeline and reporting

This commit is contained in:
JennyNou 2026-01-12 16:58:05 -05:00
parent d56629b346
commit 3202f4d933
5 changed files with 4143 additions and 242 deletions

View file

@ -8,21 +8,28 @@ schedules:
- develop - develop
pool: 'Default' pool: 'Default'
resources:
repositories:
- repository: AzureDevOps
type: github
name: Safelite/AzureDevOps
endpoint: Safelite
ref: refs/tags/t5.7.40
variables: variables:
# - group: Digital-Infrastructure # - group: Digital-Infrastructure
# - group: ISS-BuildBranches # - group: ISS-BuildBranches
- group: SafelitePlaywright
- name: dockerImageName - name: dockerImageName
value: 'playwright-tests' value: 'playwright-tests'
- name: imageTag - name: imageTag
value: '$(Build.BuildId)' value: '$(Build.BuildId)'
- name: totalShards - name: totalShards
value: 4 value: '4'
- name: IS_REGRESSION - name: IS_REGRESSION
value: 'true' value: 'true'
- name: JIRA_BOARD_ID - name: JIRA_BOARD_ID
value: '853' value: '853'
- name: JIRA_EPIC_KEY
value: 'INSR-414'
- name: JIRA_PROJECT_KEY - name: JIRA_PROJECT_KEY
value: 'INSR' value: 'INSR'
@ -30,158 +37,17 @@ stages:
- stage: TestPr - stage: TestPr
displayName: Run Playwright Test displayName: Run Playwright Test
jobs: jobs:
- job: playwright_tests - template: templates/digital/playwright-test.yml@AzureDevOps
continueOnError: true parameters:
strategy: applicationType: 'vue'
matrix: totalShards: ${{ variables.totalShards }}
shard1: targetUrl: $(BASE_URL)
shardNumber: 1 dockerFileName: 'Dockerfile.playwright'
shard2: isRegression: ${{ variables.IS_REGRESSION }}
shardNumber: 2 filterTags: ''
shard3: playwrightTestsPath: 'playwright-tests'
shardNumber: 3 npmServePath: '.'
shard4: npmrcPath: 'playwright-tests/.npmrc'
shardNumber: 4 secrets:
CCIS_API_AUTH: $(CCIS_API_AUTH)
steps:
- task: Docker@2
displayName: 'Build Docker Image'
inputs:
command: build
dockerfile: Dockerfile.playwright
repository: $(dockerImageName)
tags: $(imageTag)
arguments: '--no-cache --pull'
- script: |
# 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 \"@smoke|@Advanced\"")
# 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: |
# 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 IS_REGRESSION=$(IS_REGRESSION) \
-e JIRA_BOARD_ID=$(JIRA_BOARD_ID) \
-e JIRA_EPIC_KEY=$(JIRA_EPIC_KEY) \
-e JIRA_PROJECT_KEY=$(JIRA_PROJECT_KEY) \
$(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='playwright-tests/impl/reporter/JiraWritebackReporter.ts',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 ")
# 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) 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()

3
playwright-tests/.npmrc Normal file
View file

@ -0,0 +1,3 @@
registry=https://pkgs.dev.azure.com/Safelite/Digital/_packaging/DigitalQA/npm/registry/
always-auth=true

4013
playwright-tests/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
{
"name": "safelite-iss-nextgen-playwright-tests",
"version": "1.0.0",
"description": "Playwright tests for ISS Nextgen using Safelite Playwright Core",
"directories": {
"test": "tests"
},
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"test:debug": "playwright test --debug",
"test:smoke": "playwright test --grep @smoke",
"test:report": "playwright test --grep @test_report",
"report": "playwright show-report",
"install": "playwright install",
"install:deps": "playwright install-deps"
},
"keywords": [],
"license": "ISC",
"dependencies": {
"axios": "^1.9.0",
"axios-retry": "^4.5.0",
"safelite-playwright-core": "^1.0.26"
},
"devDependencies": {
"@faker-js/faker": "^9.8.0",
"@playwright/test": "^1.52.0",
"@types/dotenv-safe": "^8.1.6",
"@types/node": "^22.15.32",
"dotenv-safe": "^9.1.0",
"eslint": "^9.28.0",
"luxon": "^3.6.1",
"ortoni-report": "^3.0.5",
"playwright-jira-reporter": "^1.0.16"
},
"private": "true"
}

View file

@ -1,47 +1,62 @@
import { formatDateForFilename } from 'safelite-playwright-core';
import { defineConfig, devices } from '@playwright/test'; import { defineConfig, devices } from '@playwright/test';
import { JiraReporterConfig } from 'playwright-jira-reporter'
import dotenv from 'dotenv-safe'; import dotenv from 'dotenv-safe';
import { OrtoniReportConfig } from "ortoni-report"; import { OrtoniReportConfig } from "ortoni-report";
import * as path from 'path'; import path from 'path';
if (!process.env.CI) { if (!process.env.CI) {
// Environment variables are present in CI environment, no need to read from file // Environment variables are present in CI environment, no need to read from file
if (process.env.NODE_ENV == 'undefined' || process.env.NODE_ENV == null) { const basePath = __dirname; // This gets the directory where the config file is located
dotenv.config({ path: `playwright-tests/.env.dev`, example: 'playwright-tests/.env.example' });
} if (process.env.PLAYWRIGHT_ENV == undefined || process.env.PLAYWRIGHT_ENV == null) {
else { dotenv.config({
dotenv.config({ path: `playwright-tests/.env.${process.env.NODE_ENV}`, example: 'playwright-tests/.env.example' }); path: path.join(basePath, '.env.dev'),
} example: path.join(basePath, '.env.example')
});
}
dotenv.config({
path: path.join(basePath, `.env.${process.env.PLAYWRIGHT_ENV}`),
example: path.join(basePath, '.env.example')
});
} }
// Ortoni config
const ortoniReportConfig: OrtoniReportConfig = {
open: "never",
folderPath: process.env.CI ? 'ortoni-report' : 'test-results',
title: "ISS-NextGen Test Report",
filename: `iss_nextgen_ortoni_report_${formatDateForFilename(new Date())}.html`,
showProject: false,
projectName: "ISS-NextGen-Playwright-Report",
testType: `E2E- Environment: ${process.env.PLAYWRIGHT_ENV} `,
preferredTheme: "light",
base64Image: true,
}
/** // Jira Report config
* Read environment variables from file. const jiraReportConfig: JiraReporterConfig = {
* https://github.com/motdotla/dotenv // Jira Reporter Config
*/ isRegressionRun: process.env.IS_REGRESSION === 'true',
// import dotenv from 'dotenv'; jiraProjectKey: process.env.JIRA_PROJECT_KEY || '',
// import path from 'path'; jiraEpicKey: process.env.JIRA_EPIC_KEY || '',
// dotenv.config({ path: path.resolve(__dirname, '.env') }); jiraCardNumber: process.env.JIRA_CARD_NUMBER || '',
applicationName: 'ISS NextGen',
/** jiraApiUtilConfig: {
* See https://playwright.dev/docs/test-configuration. jiraUrl: process.env.JIRA_SERVER || '',
*/ jiraUsername: process.env.JIRA_USERNAME || '',
const reportConfig: OrtoniReportConfig = { jiraApiKey: process.env.JIRA_API_KEY || '',
port: 1994, jiraBoardId: process.env.JIRA_BOARD_ID || ''
open: "never", },
folderPath: "test-results", jiraCreationPermissions: {
filename: "index.html", isCreateTestSubtasks: process.env.IS_REGRESSION !== 'true',
logo: "../business-logic/data/logo.png", isCreateBugs: process.env.IS_REGRESSION !== 'true'
title: "Test Report", },
showProject: false, // Wrapped ortoni config
projectName: "ISS-Nextgen-Playwright-Report", ...ortoniReportConfig
testType: `E2E- Environment: ${process.env.NODE_ENV} `,
preferredTheme: "light",
base64Image: true,
}; };
export const reportFilePath = path.resolve(__dirname, './../test-results/accessibility-report.html');
export const jsonReportFilePath = path.resolve(__dirname, './../test-results/accessibility-report.json');
export default defineConfig({ export default defineConfig({
testDir: './tests', testDir: './tests',
/* Run tests in files in parallel */ /* Run tests in files in parallel */
@ -53,12 +68,16 @@ export default defineConfig({
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 4 : 5, workers: process.env.CI ? 4 : 5,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [ reporter: process.env.CI? [
['ortoni-report', reportConfig], ['junit'],
['playwright-jira-reporter', jiraReportConfig],
['ortoni-report', ortoniReportConfig]
]: [
['ortoni-report', ortoniReportConfig],
['junit'], ['junit'],
['list'] ['list']
], ],
timeout: 120_000, timeout: 240_000,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: { use: {
/* Base URL to use in actions like `await page.goto('/')`. */ /* Base URL to use in actions like `await page.goto('/')`. */
@ -68,52 +87,15 @@ export default defineConfig({
trace: 'on-first-retry', trace: 'on-first-retry',
headless: process.env.CI ? true : false, headless: process.env.CI ? true : false,
screenshot: "only-on-failure", screenshot: "only-on-failure",
actionTimeout: 5_000, actionTimeout: 60_000,
navigationTimeout: 20_000 navigationTimeout: 60_000
}, },
/* Configure projects for major browsers */ /* Configure projects for major browsers */
projects: [ projects: [
{ {
name: 'chromium', name: 'chromium',
use: { ...devices['Desktop Chrome'] }, use: { ...devices['Desktop Chrome'] },
}, },
],
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
}); });