diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d2c57d5ca..afbdf7bc6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,340 +36,3 @@ jobs:
node-version: 18
- run: npm ci
- run: npm test
-
- e2e_tests:
- name: Playwright Tests
- runs-on: ubuntu-latest
- permissions:
- contents: read
-
- services:
- backend:
- image: ghcr.io/ydb-platform/local-ydb:nightly
- ports:
- - 2135:2135
- - 8765:8765
- options: --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000"
-
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 18
- cache: npm
-
- - name: Install dependencies
- run: npm ci
-
- - name: Install Playwright deps
- run: npm run test:e2e:install
-
- - name: Run Playwright tests
- id: run_tests
- run: npm run test:e2e
- env:
- CI: true
- PLAYWRIGHT_VIDEO: 'on'
-
- - name: Upload Playwright artifacts
- if: always()
- uses: actions/upload-artifact@v3
- with:
- name: playwright-artifacts
- path: playwright-artifacts
- retention-days: 5
-
- - name: Get test results
- if: always()
- id: test-results
- run: |
- echo "Current directory: $(pwd)"
- echo "Listing playwright-artifacts directory:"
- ls -R playwright-artifacts
-
- if [ -f "playwright-artifacts/test-results.json" ]; then
- echo "Parsing JSON file:"
- total=$(jq '.stats.expected + .stats.unexpected + .stats.flaky + .stats.skipped' playwright-artifacts/test-results.json)
- passed=$(jq '.stats.expected' playwright-artifacts/test-results.json)
- failed=$(jq '.stats.unexpected' playwright-artifacts/test-results.json)
- flaky=$(jq '.stats.flaky' playwright-artifacts/test-results.json)
- skipped=$(jq '.stats.skipped' playwright-artifacts/test-results.json)
-
- echo "Parsed values:"
- echo "Total: $total"
- echo "Passed: $passed"
- echo "Failed: $failed"
- echo "Flaky: $flaky"
- echo "Skipped: $skipped"
- else
- echo "test-results.json file not found"
- total=0
- passed=0
- failed=0
- flaky=0
- skipped=0
- fi
-
- echo "total=$total" >> $GITHUB_OUTPUT
- echo "passed=$passed" >> $GITHUB_OUTPUT
- echo "failed=$failed" >> $GITHUB_OUTPUT
- echo "flaky=$flaky" >> $GITHUB_OUTPUT
- echo "skipped=$skipped" >> $GITHUB_OUTPUT
-
- bundle_size:
- name: Check Bundle Size
- runs-on: ubuntu-latest
- if: ${{github.event.action != 'checks_requested'}}
- outputs:
- current_size: ${{ steps.current_size.outputs.size }}
- main_size: ${{ steps.main_size.outputs.size }}
- diff: ${{ steps.size_diff.outputs.diff }}
- percent: ${{ steps.size_diff.outputs.percent }}
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 18
- cache: npm
-
- - name: Install dependencies
- run: npm ci
-
- - name: Build bundle (current branch)
- run: npm run build
-
- - name: Get current bundle size
- id: current_size
- run: |
- size=$(du -sb build | cut -f1)
- echo "size=$size" >> $GITHUB_OUTPUT
-
- - name: Checkout main branch
- uses: actions/checkout@v4
- with:
- ref: main
-
- - name: Install dependencies (main)
- run: npm ci
-
- - name: Build bundle (main branch)
- run: npm run build
-
- - name: Get main bundle size
- id: main_size
- run: |
- size=$(du -sb build | cut -f1)
- echo "size=$size" >> $GITHUB_OUTPUT
-
- - name: Calculate size difference
- id: size_diff
- run: |
- current=${{ steps.current_size.outputs.size }}
- main=${{ steps.main_size.outputs.size }}
- diff=$((current - main))
- if [ "$main" -ne "0" ]; then
- percent=$(awk "BEGIN {printf \"%.2f\", ($diff/$main) * 100}")
- else
- percent="N/A"
- fi
- echo "diff=$diff" >> $GITHUB_OUTPUT
- echo "percent=$percent" >> $GITHUB_OUTPUT
-
- deploy_report:
- name: Deploy Test Report
- needs: [e2e_tests]
- if: ${{always() && (github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))}}
- runs-on: ubuntu-latest
- permissions:
- contents: write
- pages: write
-
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Fetch gh-pages branch
- run: |
- git fetch origin gh-pages:gh-pages
- mkdir gh-pages
- git --work-tree=gh-pages checkout gh-pages -- .
-
- - name: Download Playwright artifacts
- uses: actions/download-artifact@v3
- with:
- name: playwright-artifacts
- path: playwright-artifacts
-
- - name: Copy new report
- run: |
- if [ "${{ github.event_name }}" = "pull_request" ]; then
- REPORT_DIR="${{ github.event.pull_request.number }}"
- else
- REPORT_DIR="main"
- fi
- rm -rf gh-pages/$REPORT_DIR
- mkdir -p gh-pages/$REPORT_DIR
- cp -r playwright-artifacts/playwright-report/* gh-pages/$REPORT_DIR/
-
- - name: Deploy report to GitHub Pages
- uses: peaceiris/actions-gh-pages@v3
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: gh-pages
- destination_dir: .
- force_orphan: true
-
- update_pr:
- name: Update PR Description
- needs: [e2e_tests, bundle_size]
- if: ${{always() && github.event_name == 'pull_request'}}
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
-
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Download Playwright artifacts
- uses: actions/download-artifact@v3
- with:
- name: playwright-artifacts
- path: playwright-artifacts
-
- - name: Count new tests
- id: count_tests
- run: |
- git fetch origin main:main
- new_tests=0
-
- # Get list of changed test files
- for file in $(git diff --name-only main...HEAD | grep -E '^tests/suites/.*\.(spec|test)\.(ts|tsx|js|jsx)$'); do
- # Count tests in current version
- if git show HEAD:"$file" > /dev/null 2>&1; then
- current_tests=$(git show HEAD:"$file" | grep -E "test\([\'\"]" | wc -l)
- else
- current_tests=0
- fi
-
- # Count tests in main version
- if git show main:"$file" > /dev/null 2>&1; then
- base_tests=$(git show main:"$file" | grep -E "test\([\'\"]" | wc -l)
- else
- base_tests=0
- fi
-
- # Add difference to total
- ((new_tests += current_tests - base_tests))
- done
-
- echo "new_tests=$new_tests" >> $GITHUB_OUTPUT
-
- - name: Update PR description
- uses: actions/github-script@v6
- with:
- github-token: ${{secrets.GITHUB_TOKEN}}
- script: |
- const fs = require('fs');
- const testResultsPath = 'playwright-artifacts/test-results.json';
- let testResults;
-
- if (fs.existsSync(testResultsPath)) {
- const rawData = fs.readFileSync(testResultsPath);
- const data = JSON.parse(rawData);
- testResults = {
- total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped,
- passed: data.stats.expected,
- failed: data.stats.unexpected,
- flaky: data.stats.flaky,
- skipped: data.stats.skipped
- };
- } else {
- console.log('Test results file not found');
- testResults = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0 };
- }
-
- const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`;
- const status = testResults.failed > 0 ? '❌ FAILED' : (testResults.flaky > 0 ? '⚠️ FLAKY' : '✅ PASSED');
- const statusColor = testResults.failed > 0 ? 'red' : (testResults.flaky > 0 ? 'orange' : 'green');
-
- const currentSize = parseInt('${{ needs.bundle_size.outputs.current_size }}');
- const mainSize = parseInt('${{ needs.bundle_size.outputs.main_size }}');
- const diff = parseInt('${{ needs.bundle_size.outputs.diff }}');
- const percent = '${{ needs.bundle_size.outputs.percent }}';
-
- const formatSize = (size) => {
- if (size >= 1024) {
- return `${(size / (1024 * 1024)).toFixed(2)} MB`;
- }
- return `${(size / 1024).toFixed(2)} KB`;
- };
-
- const bundleStatus = percent === 'N/A' ? '⚠️' :
- parseFloat(percent) > 0 ? '🔺' :
- parseFloat(percent) < 0 ? '🔽' : '✅';
-
- const newTests = parseInt('${{ steps.count_tests.outputs.new_tests }}');
- const testsStatus = newTests > 0 ? '✨' : '➖';
-
- const ciSection = `## CI Results
-
- ### Test Status: ${status}
- 📊 [Full Report](${reportUrl})
-
- | Total | Passed | Failed | Flaky | Skipped | New Tests |
- |:-----:|:------:|:------:|:-----:|:-------:|:---------:|
- | ${testResults.total} | ${testResults.passed} | ${testResults.failed} | ${testResults.flaky} | ${testResults.skipped} | ${testsStatus} ${newTests} |
-
- ### Bundle Size: ${bundleStatus}
- Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)}
- Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`})
-
- ${
- percent === 'N/A' ? '⚠️ Unable to calculate change.' :
- parseFloat(percent) > 0 ? '⚠️ Bundle size increased. Please review.' :
- parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.'
- }
-
-
- ℹ️ CI Information
-
- - Test recordings for failed tests are available in the full report.
- - Bundle size is measured for the entire 'dist' directory.
- - 📊 indicates links to detailed reports.
- - 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size.
- - ${testsStatus} indicates ${newTests} new test cases added in this PR.
- `;
-
- const { data: pullRequest } = await github.rest.pulls.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- pull_number: context.issue.number,
- });
-
- const currentBody = pullRequest.body || '';
- const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/;
-
- let newBody = currentBody;
- if (ciSectionRegex.test(newBody)) {
- newBody = newBody.replace(ciSectionRegex, ciSection);
- } else {
- newBody += '\n\n' + ciSection;
- }
-
- await github.rest.pulls.update({
- owner: context.repo.owner,
- repo: context.repo.repo,
- pull_number: context.issue.number,
- body: newBody,
- });
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
new file mode 100644
index 000000000..efc40c1f2
--- /dev/null
+++ b/.github/workflows/quality.yml
@@ -0,0 +1,345 @@
+name: Quality Checks
+
+on:
+ pull_request:
+ branches: ['**']
+ push:
+ branches: [main]
+
+jobs:
+ e2e_tests:
+ name: Playwright Tests
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+
+ services:
+ backend:
+ image: ghcr.io/ydb-platform/local-ydb:nightly
+ ports:
+ - 2135:2135
+ - 8765:8765
+ options: --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000"
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Install Playwright deps
+ run: npm run test:e2e:install
+
+ - name: Run Playwright tests
+ id: run_tests
+ run: npm run test:e2e
+ env:
+ CI: true
+ PLAYWRIGHT_VIDEO: 'on'
+
+ - name: Upload Playwright artifacts
+ if: always()
+ uses: actions/upload-artifact@v3
+ with:
+ name: playwright-artifacts
+ path: playwright-artifacts
+ retention-days: 5
+
+ - name: Get test results
+ if: always()
+ id: test-results
+ run: |
+ echo "Current directory: $(pwd)"
+ echo "Listing playwright-artifacts directory:"
+ ls -R playwright-artifacts
+
+ if [ -f "playwright-artifacts/test-results.json" ]; then
+ echo "Parsing JSON file:"
+ total=$(jq '.stats.expected + .stats.unexpected + .stats.flaky + .stats.skipped' playwright-artifacts/test-results.json)
+ passed=$(jq '.stats.expected' playwright-artifacts/test-results.json)
+ failed=$(jq '.stats.unexpected' playwright-artifacts/test-results.json)
+ flaky=$(jq '.stats.flaky' playwright-artifacts/test-results.json)
+ skipped=$(jq '.stats.skipped' playwright-artifacts/test-results.json)
+
+ echo "Parsed values:"
+ echo "Total: $total"
+ echo "Passed: $passed"
+ echo "Failed: $failed"
+ echo "Flaky: $flaky"
+ echo "Skipped: $skipped"
+ else
+ echo "test-results.json file not found"
+ total=0
+ passed=0
+ failed=0
+ flaky=0
+ skipped=0
+ fi
+
+ echo "total=$total" >> $GITHUB_OUTPUT
+ echo "passed=$passed" >> $GITHUB_OUTPUT
+ echo "failed=$failed" >> $GITHUB_OUTPUT
+ echo "flaky=$flaky" >> $GITHUB_OUTPUT
+ echo "skipped=$skipped" >> $GITHUB_OUTPUT
+
+ bundle_size:
+ name: Check Bundle Size
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+ outputs:
+ current_size: ${{ steps.current_size.outputs.size }}
+ main_size: ${{ steps.main_size.outputs.size }}
+ diff: ${{ steps.size_diff.outputs.diff }}
+ percent: ${{ steps.size_diff.outputs.percent }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build bundle (current branch)
+ run: npm run build
+
+ - name: Get current bundle size
+ id: current_size
+ run: |
+ size=$(du -sb build | cut -f1)
+ echo "size=$size" >> $GITHUB_OUTPUT
+
+ - name: Checkout main branch
+ uses: actions/checkout@v4
+ with:
+ ref: main
+
+ - name: Install dependencies (main)
+ run: npm ci
+
+ - name: Build bundle (main branch)
+ run: npm run build
+
+ - name: Get main bundle size
+ id: main_size
+ run: |
+ size=$(du -sb build | cut -f1)
+ echo "size=$size" >> $GITHUB_OUTPUT
+
+ - name: Calculate size difference
+ id: size_diff
+ run: |
+ current=${{ steps.current_size.outputs.size }}
+ main=${{ steps.main_size.outputs.size }}
+ diff=$((current - main))
+ if [ "$main" -ne "0" ]; then
+ percent=$(awk "BEGIN {printf \"%.2f\", ($diff/$main) * 100}")
+ else
+ percent="N/A"
+ fi
+ echo "diff=$diff" >> $GITHUB_OUTPUT
+ echo "percent=$percent" >> $GITHUB_OUTPUT
+
+ deploy_report:
+ name: Deploy Test Report
+ needs: [e2e_tests]
+ if: always() && (github.event_name == 'push' && github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pages: write
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Fetch gh-pages branch
+ run: |
+ git fetch origin gh-pages:gh-pages
+ mkdir gh-pages
+ git --work-tree=gh-pages checkout gh-pages -- .
+
+ - name: Download Playwright artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: playwright-artifacts
+ path: playwright-artifacts
+
+ - name: Copy new report
+ run: |
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
+ REPORT_DIR="${{ github.event.pull_request.number }}"
+ else
+ REPORT_DIR="main"
+ fi
+ rm -rf gh-pages/$REPORT_DIR
+ mkdir -p gh-pages/$REPORT_DIR
+ cp -r playwright-artifacts/playwright-report/* gh-pages/$REPORT_DIR/
+
+ - name: Deploy report to GitHub Pages
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: gh-pages
+ destination_dir: .
+ force_orphan: true
+
+ update_pr:
+ name: Update PR Description
+ needs: [e2e_tests, bundle_size]
+ if: always() && github.event_name == 'pull_request'
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Download Playwright artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: playwright-artifacts
+ path: playwright-artifacts
+
+ - name: Count new tests
+ id: count_tests
+ run: |
+ git fetch origin main:main
+ new_tests=0
+
+ # Get list of changed test files
+ for file in $(git diff --name-only main...HEAD | grep -E '^tests/suites/.*\.(spec|test)\.(ts|tsx|js|jsx)$'); do
+ # Count tests in current version
+ if git show HEAD:"$file" > /dev/null 2>&1; then
+ current_tests=$(git show HEAD:"$file" | grep -E "test\([\'\"]" | wc -l)
+ else
+ current_tests=0
+ fi
+
+ # Count tests in main version
+ if git show main:"$file" > /dev/null 2>&1; then
+ base_tests=$(git show main:"$file" | grep -E "test\([\'\"]" | wc -l)
+ else
+ base_tests=0
+ fi
+
+ # Add difference to total
+ ((new_tests += current_tests - base_tests))
+ done
+
+ echo "new_tests=$new_tests" >> $GITHUB_OUTPUT
+
+ - name: Update PR description
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ const fs = require('fs');
+ const testResultsPath = 'playwright-artifacts/test-results.json';
+ let testResults;
+
+ if (fs.existsSync(testResultsPath)) {
+ const rawData = fs.readFileSync(testResultsPath);
+ const data = JSON.parse(rawData);
+ testResults = {
+ total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped,
+ passed: data.stats.expected,
+ failed: data.stats.unexpected,
+ flaky: data.stats.flaky,
+ skipped: data.stats.skipped
+ };
+ } else {
+ console.log('Test results file not found');
+ testResults = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0 };
+ }
+
+ const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`;
+ const status = testResults.failed > 0 ? '❌ FAILED' : (testResults.flaky > 0 ? '⚠️ FLAKY' : '✅ PASSED');
+ const statusColor = testResults.failed > 0 ? 'red' : (testResults.flaky > 0 ? 'orange' : 'green');
+
+ const currentSize = parseInt('${{ needs.bundle_size.outputs.current_size }}');
+ const mainSize = parseInt('${{ needs.bundle_size.outputs.main_size }}');
+ const diff = parseInt('${{ needs.bundle_size.outputs.diff }}');
+ const percent = '${{ needs.bundle_size.outputs.percent }}';
+
+ const formatSize = (size) => {
+ if (size >= 1024) {
+ return `${(size / (1024 * 1024)).toFixed(2)} MB`;
+ }
+ return `${(size / 1024).toFixed(2)} KB`;
+ };
+
+ const bundleStatus = percent === 'N/A' ? '⚠️' :
+ parseFloat(percent) > 0 ? '🔺' :
+ parseFloat(percent) < 0 ? '🔽' : '✅';
+
+ const newTests = parseInt('${{ steps.count_tests.outputs.new_tests }}');
+ const testsStatus = newTests > 0 ? '✨' : '➖';
+
+ const ciSection = `## CI Results
+
+ ### Test Status: ${status}
+ 📊 [Full Report](${reportUrl})
+
+ | Total | Passed | Failed | Flaky | Skipped | New Tests |
+ |:-----:|:------:|:------:|:-----:|:-------:|:---------:|
+ | ${testResults.total} | ${testResults.passed} | ${testResults.failed} | ${testResults.flaky} | ${testResults.skipped} | ${testsStatus} ${newTests} |
+
+ ### Bundle Size: ${bundleStatus}
+ Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)}
+ Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`})
+
+ ${
+ percent === 'N/A' ? '⚠️ Unable to calculate change.' :
+ parseFloat(percent) > 0 ? '⚠️ Bundle size increased. Please review.' :
+ parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.'
+ }
+
+
+ ℹ️ CI Information
+
+ - Test recordings for failed tests are available in the full report.
+ - Bundle size is measured for the entire 'dist' directory.
+ - 📊 indicates links to detailed reports.
+ - 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size.
+ - ${testsStatus} indicates ${newTests} new test cases added in this PR.
+ `;
+
+ const { data: pullRequest } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number,
+ });
+
+ const currentBody = pullRequest.body || '';
+ const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/;
+
+ let newBody = currentBody;
+ if (ciSectionRegex.test(newBody)) {
+ newBody = newBody.replace(ciSectionRegex, ciSection);
+ } else {
+ newBody += '\n\n' + ciSection;
+ }
+
+ await github.rest.pulls.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number,
+ body: newBody,
+ });