Skip to content

Commit aee4b28

Browse files
Merge branch '26_1' into 26_1_rxjs_remove
2 parents 26a7bc3 + 93225f1 commit aee4b28

15 files changed

Lines changed: 982 additions & 15 deletions

File tree

.github/workflows/visual-tests-demos.yml

Lines changed: 218 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,6 @@ jobs:
632632
uses: ./.github/actions/setup-chrome
633633
with:
634634
chrome-version: '145.0.7632.67'
635-
runner-type: 'github-hosted'
636635

637636
- name: Use Node.js
638637
uses: actions/setup-node@v4
@@ -787,7 +786,6 @@ jobs:
787786
uses: ./.github/actions/setup-chrome
788787
with:
789788
chrome-version: '145.0.7632.67'
790-
runner-type: 'github-hosted'
791789

792790
- name: Use Node.js
793791
uses: actions/setup-node@v4
@@ -916,7 +914,6 @@ jobs:
916914
uses: ./.github/actions/setup-chrome
917915
with:
918916
chrome-version: '145.0.7632.67'
919-
runner-type: 'github-hosted'
920917

921918
- name: Use Node.js
922919
uses: actions/setup-node@v4
@@ -1069,3 +1066,221 @@ jobs:
10691066
pattern: accessibility-reports-*
10701067
delete-merged: true
10711068

1069+
csp-check-jquery:
1070+
name: CSP check (jQuery)
1071+
needs: [check-should-run, build-devextreme]
1072+
if: |
1073+
always() &&
1074+
needs.check-should-run.outputs.should-run == 'true' &&
1075+
needs.build-devextreme.result == 'success'
1076+
runs-on: devextreme-shr2
1077+
timeout-minutes: 60
1078+
1079+
steps:
1080+
- name: Get sources
1081+
uses: actions/checkout@v4
1082+
1083+
- name: Download artifacts
1084+
uses: actions/download-artifact@v4
1085+
with:
1086+
name: devextreme-artifacts-jquery
1087+
path: ./packages/devextreme
1088+
1089+
- name: Unpack artifacts
1090+
working-directory: ./packages/devextreme
1091+
run: 7z x artifacts.zip -aoa
1092+
1093+
- name: Setup Chrome
1094+
uses: ./.github/actions/setup-chrome
1095+
with:
1096+
chrome-version: '145.0.7632.67'
1097+
1098+
- name: Use Node.js
1099+
uses: actions/setup-node@v4
1100+
with:
1101+
node-version: '20'
1102+
1103+
- uses: pnpm/action-setup@v4
1104+
with:
1105+
run_install: false
1106+
1107+
- name: Get pnpm store directory
1108+
shell: bash
1109+
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
1110+
1111+
- uses: actions/cache/restore@v4
1112+
name: Restore pnpm cache
1113+
with:
1114+
path: ${{ env.STORE_PATH }}
1115+
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
1116+
restore-keys: |
1117+
${{ runner.os }}-pnpm-cache
1118+
1119+
- name: Install dependencies
1120+
run: pnpm install --frozen-lockfile
1121+
1122+
- name: Start CSP Server
1123+
run: node apps/demos/utils/server/csp-server.js 8080 &
1124+
1125+
- name: Run CSP Check
1126+
working-directory: apps/demos
1127+
env:
1128+
CSP_FRAMEWORKS: jQuery
1129+
CHROME_PATH: google-chrome-stable
1130+
run: node utils/server/csp-check.js
1131+
1132+
- name: Upload CSP report
1133+
if: always()
1134+
uses: actions/upload-artifact@v4
1135+
with:
1136+
name: csp-violations-jquery
1137+
path: apps/demos/csp-reports/
1138+
if-no-files-found: ignore
1139+
1140+
csp-check-frameworks:
1141+
name: CSP check (${{ matrix.FRAMEWORK }})
1142+
needs: [check-should-run, determine-framework-tests-scope, build-devextreme]
1143+
if: |
1144+
always() &&
1145+
needs.check-should-run.outputs.should-run == 'true' &&
1146+
needs.determine-framework-tests-scope.result == 'success' &&
1147+
needs.determine-framework-tests-scope.outputs.framework-tests-scope != 'none' &&
1148+
needs.build-devextreme.result == 'success'
1149+
strategy:
1150+
fail-fast: false
1151+
matrix:
1152+
FRAMEWORK: [React, Vue, Angular]
1153+
runs-on: devextreme-shr2
1154+
timeout-minutes: 60
1155+
1156+
steps:
1157+
- name: Get sources
1158+
uses: actions/checkout@v4
1159+
1160+
- name: Download devextreme sources
1161+
uses: actions/download-artifact@v4
1162+
with:
1163+
name: devextreme-sources
1164+
1165+
- name: Setup Chrome
1166+
uses: ./.github/actions/setup-chrome
1167+
with:
1168+
chrome-version: '145.0.7632.67'
1169+
1170+
- name: Use Node.js
1171+
uses: actions/setup-node@v4
1172+
with:
1173+
node-version: '20'
1174+
1175+
- uses: pnpm/action-setup@v4
1176+
with:
1177+
run_install: false
1178+
1179+
- name: Get pnpm store directory
1180+
shell: bash
1181+
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
1182+
1183+
- uses: actions/cache/restore@v4
1184+
name: Restore pnpm cache
1185+
with:
1186+
path: ${{ env.STORE_PATH }}
1187+
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
1188+
restore-keys: |
1189+
${{ runner.os }}-pnpm-cache
1190+
1191+
- name: Install dependencies
1192+
run: pnpm install --frozen-lockfile
1193+
1194+
- name: Install tgz
1195+
working-directory: apps/demos
1196+
run: pnpm add ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz
1197+
1198+
- name: Start CSP Server
1199+
run: node apps/demos/utils/server/csp-server.js 8080 &
1200+
1201+
- name: Run CSP Check
1202+
working-directory: apps/demos
1203+
env:
1204+
CSP_FRAMEWORKS: ${{ matrix.FRAMEWORK }}
1205+
CHROME_PATH: google-chrome-stable
1206+
run: node utils/server/csp-check.js
1207+
1208+
- name: Upload CSP report
1209+
if: always()
1210+
uses: actions/upload-artifact@v4
1211+
with:
1212+
name: csp-violations-${{ matrix.FRAMEWORK }}
1213+
path: apps/demos/csp-reports/
1214+
if-no-files-found: ignore
1215+
1216+
csp-report-summary:
1217+
name: CSP Violations Summary
1218+
runs-on: devextreme-shr2
1219+
needs: [check-should-run, csp-check-jquery, csp-check-frameworks]
1220+
if: always() && needs.check-should-run.outputs.should-run == 'true'
1221+
timeout-minutes: 5
1222+
1223+
steps:
1224+
- name: Get sources
1225+
uses: actions/checkout@v4
1226+
1227+
- name: Use Node.js
1228+
uses: actions/setup-node@v4
1229+
with:
1230+
node-version: '20'
1231+
1232+
- name: Download all CSP reports
1233+
uses: actions/download-artifact@v4
1234+
with:
1235+
pattern: csp-violations-*
1236+
path: csp-reports-all
1237+
merge-multiple: true
1238+
continue-on-error: true
1239+
1240+
- name: Summarize CSP violations
1241+
run: |
1242+
mkdir -p apps/demos/csp-reports
1243+
1244+
echo "## CSP Violations Report" >> $GITHUB_STEP_SUMMARY
1245+
echo '' >> $GITHUB_STEP_SUMMARY
1246+
1247+
GRAND_TOTAL=0
1248+
for report in csp-reports-all/csp-violations-*.jsonl; do
1249+
[ -f "$report" ] || continue
1250+
FRAMEWORK=$(basename "$report" | sed 's/csp-violations-//;s/\.jsonl//')
1251+
cp "$report" "apps/demos/csp-reports/"
1252+
1253+
if [ -s "$report" ]; then
1254+
COUNT=$(wc -l < "$report" | tr -d ' ')
1255+
GRAND_TOTAL=$((GRAND_TOTAL + COUNT))
1256+
echo "### ⚠️ ${FRAMEWORK}: ${COUNT} violation(s)" >> $GITHUB_STEP_SUMMARY
1257+
echo '' >> $GITHUB_STEP_SUMMARY
1258+
echo '<details>' >> $GITHUB_STEP_SUMMARY
1259+
echo '<summary>Show detailed report</summary>' >> $GITHUB_STEP_SUMMARY
1260+
echo '' >> $GITHUB_STEP_SUMMARY
1261+
echo '```' >> $GITHUB_STEP_SUMMARY
1262+
CSP_REPORT_FILE="$report" node apps/demos/utils/server/csp-report-summary.js >> $GITHUB_STEP_SUMMARY
1263+
echo '```' >> $GITHUB_STEP_SUMMARY
1264+
echo '' >> $GITHUB_STEP_SUMMARY
1265+
echo '</details>' >> $GITHUB_STEP_SUMMARY
1266+
echo '' >> $GITHUB_STEP_SUMMARY
1267+
else
1268+
echo "### ✅ ${FRAMEWORK}: No violations" >> $GITHUB_STEP_SUMMARY
1269+
echo '' >> $GITHUB_STEP_SUMMARY
1270+
fi
1271+
done
1272+
1273+
if [ "$GRAND_TOTAL" -eq 0 ]; then
1274+
echo "✅ No CSP violations detected across all frameworks."
1275+
else
1276+
echo "⚠️ Total: $GRAND_TOTAL CSP violation(s)"
1277+
fi
1278+
1279+
- name: Upload merged CSP reports
1280+
if: always()
1281+
uses: actions/upload-artifact@v4
1282+
with:
1283+
name: csp-violations-report
1284+
path: apps/demos/csp-reports/
1285+
if-no-files-found: ignore
1286+

apps/demos/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,7 @@ Demos/**/tsconfig.json
3333
**/.DS_Store
3434
publish-demos
3535

36+
csp-reports
37+
3638
.angular
3739
angular.json

apps/demos/Demos/DataGrid/PDFExportImages/jQuery/index.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111
integrity="sha512-+EeCylkt9WHJk5tGJxYdecHOcXFRME7qnbsfeMsdQL6NUPYm2+uGFmyleEqsmVoap/f3dN/sc3BX9t9kHXkHHg=="
1212
crossorigin="anonymous"
1313
></script>
14-
<script>
15-
window.jsPDF = window.jspdf.jsPDF;
16-
</script>
17-
1814
<script src="../../../../node_modules/jquery/dist/jquery.min.js"></script>
1915
<link rel="stylesheet" type="text/css" href="../../../../node_modules/devextreme-dist/css/dx.light.css" />
2016
<script src="../../../../node_modules/devextreme-dist/js/dx.all.js"></script>

apps/demos/Demos/DataGrid/PDFExportMultipleGrids/jQuery/index.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
integrity="sha512-+EeCylkt9WHJk5tGJxYdecHOcXFRME7qnbsfeMsdQL6NUPYm2+uGFmyleEqsmVoap/f3dN/sc3BX9t9kHXkHHg=="
1616
crossorigin="anonymous"
1717
></script>
18-
<script>
19-
window.jsPDF = window.jspdf.jsPDF;
20-
</script>
2118
<script src="data.js"></script>
2219
<script src="index.js"></script>
2320
</head>

apps/demos/Demos/Stepper/FormIntegration/jQuery/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
88
<script src="../../../../node_modules/jquery/dist/jquery.min.js"></script>
99
<link rel="stylesheet" type="text/css" href="../../../../node_modules/devextreme/dist/css/dx.light.css" />
10-
<script src="../../../../node_modules/devextreme-dist/js/dx.all.debug.js"></script>
10+
<script src="../../../../node_modules/devextreme-dist/js/dx.all.js"></script>
1111
<script src="data.js"></script>
1212
<link rel="stylesheet" type="text/css" href="styles.css" />
1313
<script src="index.js"></script>

apps/demos/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@
177177
"lint-non-demos": "pnpx nx run-many -t lint-js lint-css lint-html -p devextreme-demos",
178178
"lint": "pnpm run lint-non-demos && pnpm run lint-demos",
179179
"test-testcafe": "ts-node utils/visual-tests/testcafe-runner.ts",
180+
"test-testcafe:csp": "cross-env CSP_REPORT=true ts-node utils/visual-tests/testcafe-runner.ts",
180181
"test-testcafe:accessibility": "cross-env STRATEGY=accessibility CONSTEL=jquery node utils/visual-tests/testcafe-runner.ts",
182+
"csp-server": "node utils/server/csp-server.js 8080",
183+
"csp-check": "node utils/server/csp-check.js",
181184
"fix-lint": "prettier --write . && eslint --fix . && stylelint **/*.{css,vue} --fix",
182185
"prettier": "prettier",
183186
"build-bundles": "gulp bundles",

apps/demos/testing/common.test.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { glob } from 'glob';
22
import { join } from 'path';
3-
import { existsSync } from 'fs';
3+
import { existsSync, mkdirSync, appendFileSync } from 'fs';
44
import { createScreenshotsComparer } from 'devextreme-screenshot-comparer';
55
import { axeCheck, createReport } from '@testcafe-community/axe';
6+
import { ClientFunction } from 'testcafe';
67
import {
78
runTestAtPage,
89
shouldRunFramework,
@@ -32,6 +33,28 @@ const execTestCafeCode = (t, code) => {
3233
return testCafeFunction(t);
3334
};
3435

36+
const getClientCspViolations = ClientFunction(() => (window as any).__cspViolations || []);
37+
38+
const isCspEnabled = () => process.env.CSP_REPORT === 'true';
39+
40+
const cspReportDir = join(__dirname, '..', 'csp-reports');
41+
const cspReportFile = join(cspReportDir, 'csp-violations.jsonl');
42+
43+
const writeCspReport = (testName: string, framework: string, violations: any[]) => {
44+
if (!violations.length) return;
45+
if (!existsSync(cspReportDir)) {
46+
mkdirSync(cspReportDir, { recursive: true });
47+
}
48+
for (const v of violations) {
49+
const entry = {
50+
test: testName,
51+
framework,
52+
...v,
53+
};
54+
appendFileSync(cspReportFile, `${JSON.stringify(entry)}\n`);
55+
}
56+
};
57+
3558
const getIgnoredRules = (testName) => {
3659
const ignoredRules = [];
3760

@@ -73,7 +96,7 @@ const getIgnoredRules = (testName) => {
7396
'Diagram-UICustomization': ['aria-dialog-name', 'label'],
7497
'Diagram-WebAPIService': ['aria-dialog-name', 'label'],
7598

76-
'FileManager-BindingToEF': ['aria-command-name', 'label'],
99+
'FileManager-BindingToEF': ['aria-command-name', 'empty-table-header', 'label'],
77100
'FileManager-BindingToFileSystem': ['aria-command-name', 'empty-table-header', 'label'],
78101
'FileManager-BindingToHierarchicalStructure': ['aria-command-name', 'empty-table-header', 'label'],
79102
'FileManager-CustomThumbnails': ['aria-allowed-attr', 'aria-command-name', 'image-alt', 'label'],
@@ -103,6 +126,13 @@ const getClientScripts = () => {
103126
scripts.push({ module: 'axe-core/axe.min.js' });
104127
}
105128

129+
if (isCspEnabled()) {
130+
scripts.push(
131+
// @ts-expect-error
132+
join(__dirname, '../utils/visual-tests/inject/csp-listener.js'),
133+
);
134+
}
135+
106136
scripts.push(
107137
// @ts-expect-error
108138
join(__dirname, '../utils/visual-tests/inject/test-utils.js'),
@@ -225,9 +255,15 @@ Object.values(FRAMEWORKS).forEach((approach) => {
225255
} else {
226256
const consoleMessages = await t.getBrowserConsoleMessages();
227257
const errors = [...consoleMessages.error, ...consoleMessages.warn]
228-
.filter((e) => !knownWarnings.some((kw) => e.startsWith(kw)));
258+
.filter((e) => !knownWarnings.some((kw) => e.startsWith(kw)))
259+
.filter((e) => !e.startsWith('[CSP Violation]'));
229260
await t.expect(errors).eql([]);
230261

262+
if (isCspEnabled()) {
263+
const cspViolations = await getClientCspViolations();
264+
writeCspReport(testName, approach, cspViolations);
265+
}
266+
231267
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
232268

233269
await testScreenshot(t, takeScreenshot, `${testName}.png`, undefined, comparisonOptions);
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)