mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-12465] Connect teams channel with github CI in cron multibrowser workflow for easier raporting (#5344)
* [ACS-12465] added teams raport * [ACS-12465] raport fix 1 * [ACS-12465] raport name update * [ACS-12465] sonar fix + small changes to formatting due to size limit * [ACS-12465] review fixes 2 * [ACS-12465] added warning for >2% failure rate and new column for only excluded * [ACS-12465] changed column naming
This commit is contained in:
@@ -53,6 +53,8 @@ runs:
|
|||||||
|
|
||||||
for attempt in $(seq 1 "${SUITE_ATTEMPTS:-1}"); do
|
for attempt in $(seq 1 "${SUITE_ATTEMPTS:-1}"); do
|
||||||
echo "Running playwright tests with options $OPTIONS on browser ${PLAYWRIGHT_BROWSER} (suite attempt $attempt/${SUITE_ATTEMPTS:-1})"
|
echo "Running playwright tests with options $OPTIONS on browser ${PLAYWRIGHT_BROWSER} (suite attempt $attempt/${SUITE_ATTEMPTS:-1})"
|
||||||
|
# Clear any blob report from a previous attempt so only the final attempt is reported.
|
||||||
|
if [ -n "${PLAYWRIGHT_BLOB_DIR:-}" ]; then rm -rf -- "${PLAYWRIGHT_BLOB_DIR}"; fi
|
||||||
if E2E_TARGET=$OPTIONS npm run ci:e2e; then
|
if E2E_TARGET=$OPTIONS npm run ci:e2e; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,374 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2026 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregates Playwright blob reports produced by the cron multibrowser E2E run and posts a
|
||||||
|
* per-suite summary (passed / failed / skipped / excluded) as an Adaptive Card to a Teams channel.
|
||||||
|
*
|
||||||
|
* Expects blob artifacts downloaded under `all-blobs/blob-<browser>-<suite>/` (each containing a
|
||||||
|
* Playwright blob `report.zip`) and the Teams webhook URL in `TEAMS_WEBHOOK_URL`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const { execFileSync } = require('child_process');
|
||||||
|
|
||||||
|
const BROWSERS = ['chrome', 'firefox', 'webkit', 'msedge'];
|
||||||
|
const BLOBS_DIR = 'all-blobs';
|
||||||
|
const EXCLUDE_ROOT = path.join('e2e', 'playwright');
|
||||||
|
// Matrix suite name -> test folder, for the single suite whose folder name differs.
|
||||||
|
const SUITE_TO_FOLDER = { 'special-permissions': 'special-permissions-actions-available' };
|
||||||
|
|
||||||
|
const PLAYWRIGHT_BIN = path.join('node_modules', '.bin', 'playwright');
|
||||||
|
|
||||||
|
function emptyCounts() {
|
||||||
|
return { run: 0, passed: 0, failed: 0, skipped: 0, excluded: 0, excludedOwn: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCounts(target, source) {
|
||||||
|
target.run += source.run;
|
||||||
|
target.passed += source.passed;
|
||||||
|
target.failed += source.failed;
|
||||||
|
target.skipped += source.skipped;
|
||||||
|
target.excluded += source.excluded;
|
||||||
|
target.excludedOwn += source.excludedOwn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse `blob-<browser>-<suite>` artifact directory names. */
|
||||||
|
function parseArtifactName(dirName) {
|
||||||
|
const match = /^blob-([^-]+)-(.+)$/.exec(dirName);
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const [, browser, suite] = match;
|
||||||
|
if (!BROWSERS.includes(browser)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { browser, suite };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Merge a single artifact's blob report into JSON and return Playwright `stats`. */
|
||||||
|
function readStats(artifactDir) {
|
||||||
|
const outFile = path.join(os.tmpdir(), `pw-json-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
||||||
|
try {
|
||||||
|
execFileSync(PLAYWRIGHT_BIN, ['merge-reports', artifactDir, '--reporter', 'json'], {
|
||||||
|
stdio: ['ignore', 'ignore', 'inherit'],
|
||||||
|
maxBuffer: 1 << 28,
|
||||||
|
env: { ...process.env, PLAYWRIGHT_JSON_OUTPUT_NAME: outFile }
|
||||||
|
});
|
||||||
|
const report = JSON.parse(fs.readFileSync(outFile, 'utf8'));
|
||||||
|
const stats = report.stats || {};
|
||||||
|
const passed = (stats.expected || 0) + (stats.flaky || 0);
|
||||||
|
const failed = stats.unexpected || 0;
|
||||||
|
const skipped = stats.skipped || 0;
|
||||||
|
return { run: passed + failed + skipped, passed, failed, skipped, excluded: 0, excludedOwn: 0 };
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Could not read blob report in ${artifactDir}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(outFile, { force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Count excluded test cases for a suite/browser, mirroring getExcludedTestsRegExpArray. */
|
||||||
|
function excludedForBrowser(excludeJson, browser) {
|
||||||
|
const browserKeys = ['firefox', 'chromium', 'webkit', 'msedge'];
|
||||||
|
const allCount = excludeJson.all && typeof excludeJson.all === 'object' ? Object.keys(excludeJson.all).length : 0;
|
||||||
|
|
||||||
|
let ownCount = 0;
|
||||||
|
const key = browserKeys.find((k) => k.toLowerCase() === browser.toLowerCase());
|
||||||
|
if (key && excludeJson[key] && typeof excludeJson[key] === 'object') {
|
||||||
|
// Browser-specific ids not already covered by the 'all' bucket.
|
||||||
|
const allIds = new Set(excludeJson.all ? Object.keys(excludeJson.all) : []);
|
||||||
|
ownCount = Object.keys(excludeJson[key]).filter((id) => !allIds.has(id)).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { total: allCount + ownCount, own: ownCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
function readExcludeJson(suite) {
|
||||||
|
const folder = SUITE_TO_FOLDER[suite] || suite;
|
||||||
|
const file = path.join(EXCLUDE_ROOT, folder, 'exclude.tests.json');
|
||||||
|
try {
|
||||||
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collect() {
|
||||||
|
const suiteStats = new Map();
|
||||||
|
const browserStats = new Map(BROWSERS.map((b) => [b, emptyCounts()]));
|
||||||
|
const browserSuiteStats = new Map(BROWSERS.map((b) => [b, new Map()]));
|
||||||
|
const suiteBrowsers = new Map();
|
||||||
|
|
||||||
|
if (!fs.existsSync(BLOBS_DIR)) {
|
||||||
|
return { suiteStats, browserStats, browserSuiteStats };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const dirName of fs.readdirSync(BLOBS_DIR)) {
|
||||||
|
const parsed = parseArtifactName(dirName);
|
||||||
|
if (!parsed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { browser, suite } = parsed;
|
||||||
|
const stats = readStats(path.join(BLOBS_DIR, dirName));
|
||||||
|
if (!stats) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suiteStats.has(suite)) {
|
||||||
|
suiteStats.set(suite, emptyCounts());
|
||||||
|
suiteBrowsers.set(suite, new Set());
|
||||||
|
}
|
||||||
|
suiteBrowsers.get(suite).add(browser);
|
||||||
|
addCounts(suiteStats.get(suite), stats);
|
||||||
|
addCounts(browserStats.get(browser), stats);
|
||||||
|
|
||||||
|
const bsMap = browserSuiteStats.get(browser);
|
||||||
|
if (!bsMap.has(suite)) {
|
||||||
|
bsMap.set(suite, emptyCounts());
|
||||||
|
}
|
||||||
|
addCounts(bsMap.get(suite), stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [suite, browsers] of suiteBrowsers) {
|
||||||
|
const excludeJson = readExcludeJson(suite);
|
||||||
|
if (!excludeJson) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let suiteExcluded = 0;
|
||||||
|
for (const browser of browsers) {
|
||||||
|
const { total, own } = excludedForBrowser(excludeJson, browser);
|
||||||
|
suiteExcluded += total;
|
||||||
|
browserStats.get(browser).excluded += total;
|
||||||
|
browserStats.get(browser).excludedOwn += own;
|
||||||
|
const bsMap = browserSuiteStats.get(browser);
|
||||||
|
if (bsMap.has(suite)) {
|
||||||
|
bsMap.get(suite).excluded += total;
|
||||||
|
bsMap.get(suite).excludedOwn += own;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suiteStats.get(suite).excluded += suiteExcluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { suiteStats, browserStats, browserSuiteStats };
|
||||||
|
}
|
||||||
|
|
||||||
|
function tableCell(text, opts = {}) {
|
||||||
|
return {
|
||||||
|
type: 'TableCell',
|
||||||
|
verticalContentAlignment: 'Center',
|
||||||
|
items: [{ type: 'TextBlock', text: String(text), wrap: true, weight: 'Bolder', size: 'Medium', horizontalAlignment: 'Center', ...opts }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBrowserTable(browserStats) {
|
||||||
|
const columns = [
|
||||||
|
{ width: 3 },
|
||||||
|
{ width: 1 },
|
||||||
|
{ width: 1 },
|
||||||
|
{ width: 1 },
|
||||||
|
{ width: 1 },
|
||||||
|
{ width: 1 },
|
||||||
|
{ width: 1 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const headerRow = {
|
||||||
|
type: 'TableRow',
|
||||||
|
cells: [
|
||||||
|
tableCell('Browser', { horizontalAlignment: 'Left' }),
|
||||||
|
tableCell('🔢'),
|
||||||
|
tableCell('✅'),
|
||||||
|
tableCell('❌'),
|
||||||
|
tableCell('⏭️'),
|
||||||
|
tableCell('🚫'),
|
||||||
|
tableCell('🚫🌐')
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const rows = BROWSERS.map((browser) => {
|
||||||
|
const c = browserStats.get(browser);
|
||||||
|
return {
|
||||||
|
type: 'TableRow',
|
||||||
|
cells: [
|
||||||
|
tableCell(browser, { horizontalAlignment: 'Left' }),
|
||||||
|
tableCell(c.run),
|
||||||
|
tableCell(c.passed, { color: 'Good' }),
|
||||||
|
tableCell(c.failed, { color: c.failed > 0 ? 'Attention' : 'Default' }),
|
||||||
|
tableCell(c.skipped, { color: c.skipped > 0 ? 'Warning' : 'Default' }),
|
||||||
|
tableCell(c.excluded),
|
||||||
|
tableCell(c.excludedOwn, { color: c.excludedOwn > 0 ? 'Warning' : 'Default' })
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'Table',
|
||||||
|
gridStyle: 'accent',
|
||||||
|
firstRowAsHeader: true,
|
||||||
|
columns,
|
||||||
|
rows: [headerRow, ...rows]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBrowserSuiteText(suiteMap) {
|
||||||
|
const lines = [];
|
||||||
|
for (const suite of [...suiteMap.keys()].sort()) {
|
||||||
|
const c = suiteMap.get(suite);
|
||||||
|
const failMark = c.failed > 0 ? `❌${c.failed}` : `${c.failed}`;
|
||||||
|
lines.push(`${suite}: 🔢${c.run} ✅${c.passed} ${failMark} ⏭️${c.skipped} 🚫${c.excluded} (own ${c.excludedOwn})`);
|
||||||
|
}
|
||||||
|
return lines.join('\n\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
const FAILURE_ALERT_THRESHOLD_PCT = 2;
|
||||||
|
|
||||||
|
/** Optional @mention block for the alert, if TEAMS_ALERT_MENTION_ID/NAME are set (needs a Teams UPN/AAD id). */
|
||||||
|
function buildMention() {
|
||||||
|
const id = process.env.TEAMS_ALERT_MENTION_ID;
|
||||||
|
const name = process.env.TEAMS_ALERT_MENTION_NAME;
|
||||||
|
if (!id || !name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tag: `<at>${name}</at>`,
|
||||||
|
entity: { type: 'mention', text: `<at>${name}</at>`, mentioned: { id, name } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCard({ suiteStats, browserStats, browserSuiteStats }) {
|
||||||
|
const total = emptyCounts();
|
||||||
|
for (const c of suiteStats.values()) {
|
||||||
|
addCounts(total, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date().toISOString().slice(0, 10);
|
||||||
|
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
||||||
|
|
||||||
|
const failedPct = total.run > 0 ? (total.failed / total.run) * 100 : 0;
|
||||||
|
const alertTriggered = failedPct > FAILURE_ALERT_THRESHOLD_PCT;
|
||||||
|
|
||||||
|
const browserContainers = BROWSERS.map((browser) => ({
|
||||||
|
type: 'Container',
|
||||||
|
id: `details-${browser}`,
|
||||||
|
isVisible: false,
|
||||||
|
separator: true,
|
||||||
|
items: [
|
||||||
|
{ type: 'TextBlock', weight: 'Bolder', text: `${browser} — per suite`, spacing: 'Medium' },
|
||||||
|
{ type: 'TextBlock', text: buildBrowserSuiteText(browserSuiteStats.get(browser)), wrap: true, size: 'Small' }
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
const browserToggleActions = BROWSERS.map((browser) => ({
|
||||||
|
type: 'Action.ToggleVisibility',
|
||||||
|
title: browser,
|
||||||
|
targetElements: [`details-${browser}`]
|
||||||
|
}));
|
||||||
|
|
||||||
|
const body = [
|
||||||
|
{ type: 'TextBlock', size: 'Large', weight: 'Bolder', text: `ACA Cron Multibrowser Workflow - ${date}` }
|
||||||
|
];
|
||||||
|
|
||||||
|
const mention = alertTriggered ? buildMention() : null;
|
||||||
|
if (alertTriggered) {
|
||||||
|
const alertText = `⚠️ Failure rate ${failedPct.toFixed(1)}% exceeds ${FAILURE_ALERT_THRESHOLD_PCT}% threshold${mention ? ` — ${mention.tag}` : ''}`;
|
||||||
|
body.push({
|
||||||
|
type: 'TextBlock',
|
||||||
|
text: alertText,
|
||||||
|
wrap: true,
|
||||||
|
weight: 'Bolder',
|
||||||
|
color: 'Attention',
|
||||||
|
size: 'Medium'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
body.push(
|
||||||
|
{
|
||||||
|
type: 'FactSet',
|
||||||
|
facts: [
|
||||||
|
{ title: 'Total run', value: String(total.run) },
|
||||||
|
{ title: 'Passed ✅', value: String(total.passed) },
|
||||||
|
{ title: 'Failed ❌', value: String(total.failed) },
|
||||||
|
{ title: 'Skipped ⏭️', value: String(total.skipped) },
|
||||||
|
{ title: 'Excluded 🚫', value: String(total.excluded) }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ type: 'TextBlock', weight: 'Bolder', text: 'Per browser', separator: true, spacing: 'Medium' },
|
||||||
|
buildBrowserTable(browserStats),
|
||||||
|
...browserContainers
|
||||||
|
);
|
||||||
|
|
||||||
|
const card = {
|
||||||
|
type: 'AdaptiveCard',
|
||||||
|
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
|
||||||
|
version: '1.5',
|
||||||
|
body,
|
||||||
|
actions: [...browserToggleActions, { type: 'Action.OpenUrl', title: 'Open run', url: runUrl }]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mention) {
|
||||||
|
card.msteams = { entities: [mention.entity] };
|
||||||
|
}
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postToTeams(card) {
|
||||||
|
const webhook = process.env.TEAMS_WEBHOOK_URL || process.env.TEAMS_E2E_WEBHOOK_URL;
|
||||||
|
if (!webhook) {
|
||||||
|
console.warn('TEAMS_WEBHOOK_URL is not set, skipping Teams notification.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
type: 'message',
|
||||||
|
attachments: [{ contentType: 'application/vnd.microsoft.card.adaptive', content: card }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(webhook, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Teams webhook returned ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
console.log(`Posted E2E report to Teams (HTTP ${response.status}).`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const results = collect();
|
||||||
|
const card = buildCard(results);
|
||||||
|
await postToTeams(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -64,3 +64,40 @@ jobs:
|
|||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||||
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
|
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
|
||||||
|
|
||||||
|
report-to-teams:
|
||||||
|
name: "Aggregate results and notify Teams"
|
||||||
|
needs: [chrome, firefox, webkit, msedge]
|
||||||
|
if: ${{ always() }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Before install
|
||||||
|
uses: ./.github/actions/before-install
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci --ignore-scripts
|
||||||
|
|
||||||
|
- name: Download all blob reports
|
||||||
|
id: download-blobs
|
||||||
|
continue-on-error: true
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: blob-*
|
||||||
|
path: all-blobs
|
||||||
|
|
||||||
|
- name: Retry blob download on failure
|
||||||
|
if: steps.download-blobs.outcome == 'failure'
|
||||||
|
continue-on-error: true
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: blob-*
|
||||||
|
path: all-blobs
|
||||||
|
|
||||||
|
- name: Aggregate results and post Teams report
|
||||||
|
continue-on-error: true
|
||||||
|
env:
|
||||||
|
TEAMS_WEBHOOK_URL: ${{ secrets.TEAMS_E2E_WEBHOOK_URL }}
|
||||||
|
run: node .github/scripts/e2e-teams-report.js
|
||||||
|
|||||||
@@ -198,6 +198,17 @@ jobs:
|
|||||||
suite-attempts: '2'
|
suite-attempts: '2'
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_BROWSER: ${{ matrix.browser }}
|
PLAYWRIGHT_BROWSER: ${{ matrix.browser }}
|
||||||
|
E2E_BLOB_REPORT: 'true'
|
||||||
|
PLAYWRIGHT_BLOB_DIR: ${{ github.workspace }}/blob-report
|
||||||
|
|
||||||
|
- name: Upload blob report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: blob-${{ matrix.browser }}-${{ matrix.e2e-suites.name }}
|
||||||
|
path: ${{ github.workspace }}/blob-report
|
||||||
|
if-no-files-found: ignore
|
||||||
|
retention-days: 3
|
||||||
|
|
||||||
- name: Debug Ingress Controller Logs
|
- name: Debug Ingress Controller Logs
|
||||||
if: always() && steps.deploy-local-acs.outcome == 'success'
|
if: always() && steps.deploy-local-acs.outcome == 'success'
|
||||||
|
|||||||
@@ -61,5 +61,11 @@ export const getReporter = (): ReporterDescription[] => {
|
|||||||
return [['html']];
|
return [['html']];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [['@reportportal/agent-js-playwright', getReportPortalConfig()], ['github']];
|
const reporters: ReporterDescription[] = [['@reportportal/agent-js-playwright', getReportPortalConfig()], ['github']];
|
||||||
|
|
||||||
|
if (env.E2E_BLOB_REPORT === 'true') {
|
||||||
|
reporters.push(['blob', { outputDir: env.PLAYWRIGHT_BLOB_DIR || 'blob-report' }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reporters;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user