Sifa professional network frontend (Next.js, React, TailwindCSS)
sifa.id/
1name: E2E Tests
2
3on:
4 push:
5 branches: [main]
6 pull_request:
7 paths:
8 - 'src/**'
9 - 'e2e/**'
10 - 'public/**'
11 - 'playwright.config.ts'
12 - 'package.json'
13 - 'pnpm-lock.yaml'
14 - 'next.config.*'
15 - 'tailwind.config.*'
16 - 'tsconfig.json'
17 - '.github/workflows/e2e.yml'
18
19jobs:
20 e2e:
21 runs-on: ubuntu-latest
22 timeout-minutes: 30
23 permissions:
24 contents: read
25 pull-requests: write
26 steps:
27 - uses: actions/checkout@v4
28
29 - uses: actions/setup-node@v4
30 with:
31 node-version: 25
32
33 - name: Install pnpm
34 run: corepack enable && corepack prepare pnpm@10 --activate
35
36 - name: Install dependencies
37 run: pnpm install --frozen-lockfile
38
39 - name: Install Playwright browsers
40 run: pnpm exec playwright install --with-deps chromium webkit
41
42 - name: Detect affected pages
43 id: affected
44 if: github.event_name == 'pull_request'
45 run: |
46 CHANGED=$(gh pr diff "${{ github.event.pull_request.number }}" --name-only)
47
48 # Shared files that affect ALL pages (layouts, components, styles, config, public assets)
49 if echo "$CHANGED" | grep -qE '^(src/app/layout\.tsx|src/app/\(main\)/layout\.tsx|src/components/|src/styles/|src/lib/|src/i18n/|public/|tailwind\.config|next\.config|src/app/globals\.css)'; then
50 echo "pages=all" >> "$GITHUB_OUTPUT"
51 echo "frontend_changed=true" >> "$GITHUB_OUTPUT"
52 exit 0
53 fi
54
55 # Map route-specific files to page names
56 PAGES=""
57 while IFS= read -r file; do
58 case "$file" in
59 src/app/\(main\)/page.tsx) PAGES="$PAGES,homepage" ;;
60 src/app/\(main\)/about/*) PAGES="$PAGES,about" ;;
61 src/app/\(main\)/privacy/*) PAGES="$PAGES,privacy" ;;
62 src/app/\(main\)/terms/*) PAGES="$PAGES,terms" ;;
63 src/app/\(main\)/login/*) PAGES="$PAGES,login" ;;
64 src/app/\(main\)/embed/*) PAGES="$PAGES,embed" ;;
65 src/app/\(embed\)/*) PAGES="$PAGES,embed" ;;
66 src/app/\(main\)/search/*) PAGES="$PAGES,search" ;;
67 src/app/\(main\)/experts/*) PAGES="$PAGES,experts" ;;
68
69 esac
70 done <<< "$CHANGED"
71
72 # Deduplicate and trim leading comma
73 PAGES=$(echo "$PAGES" | tr ',' '\n' | sort -u | paste -sd ',' - | sed 's/^,//')
74
75 if [ -z "$PAGES" ]; then
76 echo "pages=none" >> "$GITHUB_OUTPUT"
77 echo "frontend_changed=false" >> "$GITHUB_OUTPUT"
78 else
79 echo "pages=$PAGES" >> "$GITHUB_OUTPUT"
80 echo "frontend_changed=true" >> "$GITHUB_OUTPUT"
81 fi
82 env:
83 GH_TOKEN: ${{ github.token }}
84
85 - name: Build application
86 run: pnpm build
87
88 - name: Run E2E tests
89 run: pnpm exec playwright test --grep-invert "Visual regression"
90 env:
91 PLAYWRIGHT_BASE_URL: http://localhost:3000
92 QA_SCREENSHOTS: ${{ (github.event_name == 'push' || steps.affected.outputs.frontend_changed == 'true') && '1' || '0' }}
93 AFFECTED_PAGES: ${{ steps.affected.outputs.pages || 'all' }}
94
95 - name: Run visual regression (update baselines if missing)
96 if: ${{ github.event_name == 'push' || steps.affected.outputs.pages != 'none' }}
97 run: pnpm exec playwright test e2e/visual-regression.spec.ts --update-snapshots --reporter=line
98 env:
99 PLAYWRIGHT_BASE_URL: http://localhost:3000
100 AFFECTED_PAGES: ${{ steps.affected.outputs.pages || 'all' }}
101
102 - name: Upload test report
103 uses: actions/upload-artifact@v4
104 if: ${{ !cancelled() }}
105 with:
106 name: playwright-report
107 path: e2e/playwright-report/
108 retention-days: 14
109
110 - name: Upload QA screenshots
111 uses: actions/upload-artifact@v4
112 if: ${{ !cancelled() }}
113 with:
114 name: qa-screenshots
115 path: e2e/qa-screenshots/
116 retention-days: 14
117
118 - name: Publish screenshots to VPS
119 if: ${{ !cancelled() && hashFiles('e2e/qa-screenshots/**') != '' }}
120 env:
121 VPS_HOST: ${{ secrets.VPS_HOST }}
122 VPS_USER: ${{ secrets.VPS_USER }}
123 VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
124 PR_NUMBER: ${{ github.event.pull_request.number }}
125 RUN_ID: ${{ github.run_id }}
126 GH_TOKEN: ${{ github.token }}
127 run: |
128 # Set up SSH
129 mkdir -p ~/.ssh
130 echo "$VPS_SSH_KEY" > ~/.ssh/deploy_key
131 chmod 600 ~/.ssh/deploy_key
132 ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null
133
134 # Upload to VPS: /var/www/screenshots/pr-<number>-run-<id>/
135 DEST="pr-${PR_NUMBER}-run-${RUN_ID}"
136 ssh -i ~/.ssh/deploy_key "$VPS_USER@$VPS_HOST" "mkdir -p /var/www/screenshots/$DEST"
137 scp -i ~/.ssh/deploy_key -r e2e/qa-screenshots/. "$VPS_USER@$VPS_HOST:/var/www/screenshots/$DEST/"
138
139 # Build markdown comment body
140 BASE_URL="https://sifa.id/screenshots/$DEST"
141 BODY="## E2E Screenshots\n\nViewports tested: desktop-chrome, mobile-safari (iPhone 14), tablet (iPad Mini)\n\n"
142 for f in $(ssh -i ~/.ssh/deploy_key "$VPS_USER@$VPS_HOST" "find /var/www/screenshots/$DEST -name '*.png' | sort"); do
143 FILENAME=$(basename "$f")
144 LABEL="${FILENAME%.png}"
145 BODY+="### $LABEL\n\n\n"
146 done
147
148 # Post comment on the PR
149 if [ -n "$PR_NUMBER" ]; then
150 printf "%b" "$BODY" | gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body-file -
151 fi
152
153 - name: Upload test traces
154 uses: actions/upload-artifact@v4
155 if: failure()
156 with:
157 name: playwright-traces
158 path: e2e/test-results/
159 retention-days: 7
160
161 - name: Upload visual regression snapshots
162 uses: actions/upload-artifact@v4
163 if: ${{ !cancelled() }}
164 with:
165 name: visual-regression-snapshots
166 path: e2e/visual-regression.spec.ts-snapshots/
167 retention-days: 14
168
169 - name: Upload visual regression diffs
170 uses: actions/upload-artifact@v4
171 if: failure()
172 with:
173 name: visual-regression-diffs
174 path: e2e/test-results/**/*-diff.png
175 retention-days: 14