1+ # Copyright The Linux Foundation and each contributor to LFX.
2+ # SPDX-License-Identifier: MIT
3+
4+ name : E2E Tests
5+
6+ permissions :
7+ id-token : write
8+ contents : read
9+ issues : write
10+ pull-requests : write
11+
12+ on :
13+ workflow_call :
14+ inputs :
15+ node-version :
16+ description : ' Node.js version to use'
17+ required : false
18+ default : ' 22'
19+ type : string
20+ test-command :
21+ description : ' Test command to run'
22+ required : false
23+ default : ' e2e'
24+ type : string
25+ browser :
26+ description : ' Browser to test (chromium, firefox, mobile-chrome, or all)'
27+ required : false
28+ default : ' all'
29+ type : string
30+ base-url :
31+ description : ' Base URL for testing'
32+ required : false
33+ default : ' http://localhost:4200'
34+ type : string
35+ skip-build :
36+ description : ' Skip building the application (use existing build)'
37+ required : false
38+ default : false
39+ type : boolean
40+ secrets :
41+ TEST_USERNAME :
42+ description : ' Username for test authentication'
43+ required : false
44+ TEST_PASSWORD :
45+ description : ' Password for test authentication'
46+ required : false
47+ outputs :
48+ test-results :
49+ description : ' Test results summary'
50+ value : ${{ jobs.e2e-tests.outputs.test-results }}
51+ report-url :
52+ description : ' URL to the test report artifact'
53+ value : ${{ jobs.e2e-tests.outputs.report-url }}
54+
55+ jobs :
56+ e2e-tests :
57+ name : Playwright E2E Tests
58+ runs-on : ubuntu-latest
59+ timeout-minutes : 30
60+
61+ outputs :
62+ test-results : ${{ steps.test-results.outputs.results }}
63+ report-url : ${{ steps.upload-report.outputs.artifact-url }}
64+
65+ steps :
66+ - name : Checkout code
67+ uses : actions/checkout@v4
68+ with :
69+ fetch-depth : 0
70+
71+ - name : Enable Corepack
72+ run : corepack enable
73+
74+ - name : Setup Node.js
75+ uses : actions/setup-node@v4
76+ with :
77+ node-version : ${{ inputs.node-version }}
78+ cache : ' yarn'
79+
80+ - name : Install dependencies
81+ run : yarn install --immutable
82+
83+ - name : OIDC Auth
84+ uses : aws-actions/configure-aws-credentials@v4
85+ id : oidc-auth
86+ with :
87+ audience : sts.amazonaws.com
88+ role-to-assume : arn:aws:iam::788942260905:role/github-actions-deploy
89+ aws-region : us-west-2
90+
91+ - name : Read secrets from AWS Secrets Manager into environment variables
92+ id : get_secrets
93+ uses : aws-actions/aws-secretsmanager-get-secrets@v2
94+ with :
95+ secret-ids : |
96+ SUPABASE, /cloudops/managed-secrets/cloud/supabase/api_key
97+ AUTH0, /cloudops/managed-secrets/auth0/LFX_V2_PCC
98+
99+ - name : Setup Turborepo cache
100+ uses : actions/cache@v4
101+ with :
102+ path : |
103+ .turbo
104+ node_modules/.cache/turbo
105+ key : ${{ runner.os }}-turbo-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/*', '!**/node_modules/**', '!**/.turbo/**') }}
106+ restore-keys : |
107+ ${{ runner.os }}-turbo-${{ hashFiles('**/yarn.lock') }}-
108+ ${{ runner.os }}-turbo-
109+
110+ - name : Validate required secrets for E2E testing
111+ id : validate-secrets
112+ run : |
113+ missing_secrets=""
114+
115+ # Check AWS Secrets Manager secrets (masked environment variables)
116+ if [ -z "$AUTH0" ]; then
117+ missing_secrets="$missing_secrets AUTH0 (from AWS Secrets Manager)"
118+ fi
119+ if [ -z "$SUPABASE" ]; then
120+ missing_secrets="$missing_secrets SUPABASE (from AWS Secrets Manager)"
121+ fi
122+
123+ # Check GitHub secrets (fallback)
124+ if [ -z "${{ secrets.TEST_USERNAME }}" ]; then
125+ missing_secrets="$missing_secrets TEST_USERNAME"
126+ fi
127+ if [ -z "${{ secrets.TEST_PASSWORD }}" ]; then
128+ missing_secrets="$missing_secrets TEST_PASSWORD"
129+ fi
130+
131+ if [ -n "$missing_secrets" ]; then
132+ echo "❌ Missing required secrets for E2E testing:$missing_secrets"
133+ echo "Please configure these secrets to enable E2E tests."
134+ echo "can_run_tests=false" >> $GITHUB_OUTPUT
135+ else
136+ echo "✅ All required secrets are configured"
137+ echo "can_run_tests=true" >> $GITHUB_OUTPUT
138+ fi
139+
140+ - name : Set up non-sensitive environment variables
141+ if : steps.validate-secrets.outputs.can_run_tests == 'true'
142+ run : |
143+ echo "ENV=development" >> $GITHUB_ENV
144+ echo "PCC_BASE_URL=http://localhost:4200" >> $GITHUB_ENV
145+ echo "PCC_AUTH0_ISSUER_BASE_URL=https://linuxfoundation-dev.auth0.com/" >> $GITHUB_ENV
146+ echo "PCC_AUTH0_AUDIENCE=https://api-gw.dev.platform.linuxfoundation.org/" >> $GITHUB_ENV
147+ echo "CI=true" >> $GITHUB_ENV
148+
149+ - name : Set up sensitive environment variables
150+ if : steps.validate-secrets.outputs.can_run_tests == 'true'
151+ run : |
152+ # Parse and set AUTH0 secrets with explicit masking
153+ if [ -n "$AUTH0" ]; then
154+ AUTH0_CLIENT_ID=$(echo "$AUTH0" | jq -r '.client_id // empty')
155+ AUTH0_CLIENT_SECRET=$(echo "$AUTH0" | jq -r '.client_secret // empty')
156+
157+ # Explicitly mask the values
158+ echo "::add-mask::$AUTH0_CLIENT_ID"
159+ echo "::add-mask::$AUTH0_CLIENT_SECRET"
160+
161+ # Set as environment variables
162+ echo "PCC_AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID" >> $GITHUB_ENV
163+ echo "PCC_AUTH0_CLIENT_SECRET=$AUTH0_CLIENT_SECRET" >> $GITHUB_ENV
164+ echo "✅ AUTH0 secrets set as masked environment variables"
165+ fi
166+
167+ # Parse and set SUPABASE secrets
168+ if [ -n "$SUPABASE" ]; then
169+ SUPABASE_URL=$(echo "$SUPABASE" | jq -r '.url // empty')
170+ SUPABASE_API_KEY=$(echo "$SUPABASE" | jq -r '.api_key // empty')
171+
172+ # Explicitly mask the values
173+ echo "::add-mask::$SUPABASE_URL"
174+ echo "::add-mask::$SUPABASE_API_KEY"
175+
176+ # Set as environment variables
177+ echo "SUPABASE_URL=$SUPABASE_URL" >> $GITHUB_ENV
178+ echo "POSTGRES_API_KEY=$SUPABASE_API_KEY" >> $GITHUB_ENV
179+ echo "✅ SUPABASE secrets set as masked environment variables"
180+ fi
181+
182+ # Set test credentials
183+ echo "::add-mask::${{ secrets.TEST_USERNAME }}"
184+ echo "::add-mask::${{ secrets.TEST_PASSWORD }}"
185+ echo "TEST_USERNAME=${{ secrets.TEST_USERNAME }}" >> $GITHUB_ENV
186+ echo "TEST_PASSWORD=${{ secrets.TEST_PASSWORD }}" >> $GITHUB_ENV
187+
188+ - name : Install Playwright browsers
189+ if : steps.validate-secrets.outputs.can_run_tests == 'true'
190+ working-directory : apps/lfx-pcc
191+ run : npx playwright install --with-deps
192+
193+ - name : Create Playwright auth directory
194+ if : steps.validate-secrets.outputs.can_run_tests == 'true'
195+ working-directory : apps/lfx-pcc
196+ run : mkdir -p playwright/.auth
197+
198+ - name : Build the application
199+ if : steps.validate-secrets.outputs.can_run_tests == 'true'
200+ run : yarn build
201+
202+ - name : Run E2E tests (All browsers)
203+ if : ${{ inputs.browser == 'all' && steps.validate-secrets.outputs.can_run_tests == 'true' }}
204+ working-directory : apps/lfx-pcc
205+ run : |
206+ if [ -n "$TEST_USERNAME" ] && [ -n "$TEST_PASSWORD" ]; then
207+ echo "🔐 Running authenticated E2E tests on all browsers"
208+ echo "🚀 Playwright will automatically start the dev server on localhost:4200"
209+ echo "📋 Using secrets from AWS Secrets Manager"
210+ yarn ${{ inputs.test-command }} --reporter=list
211+ else
212+ echo "⚠️ No test credentials provided. Skipping E2E tests."
213+ echo "Set TEST_USERNAME and TEST_PASSWORD secrets to enable E2E tests."
214+ exit 0
215+ fi
216+
217+ - name : Run E2E tests (Specific browser)
218+ if : ${{ inputs.browser != 'all' && steps.validate-secrets.outputs.can_run_tests == 'true' }}
219+ working-directory : apps/lfx-pcc
220+ run : |
221+ if [ -n "$TEST_USERNAME" ] && [ -n "$TEST_PASSWORD" ]; then
222+ echo "🔐 Running authenticated E2E tests on ${{ inputs.browser }}"
223+ echo "🚀 Playwright will automatically start the dev server on localhost:4200"
224+ echo "📋 Using secrets from AWS Secrets Manager"
225+ yarn ${{ inputs.test-command }} --project=${{ inputs.browser }} --reporter=list
226+ else
227+ echo "⚠️ No test credentials provided. Skipping E2E tests."
228+ echo "Set TEST_USERNAME and TEST_PASSWORD secrets to enable E2E tests."
229+ exit 0
230+ fi
231+
232+ - name : E2E tests skipped
233+ if : ${{ steps.validate-secrets.outputs.can_run_tests == 'false' }}
234+ run : |
235+ echo "⏭️ E2E tests skipped due to missing required secrets"
236+ echo "Configure the following secrets to enable E2E testing:"
237+ echo ""
238+ echo "AWS Secrets Manager (required):"
239+ echo " - /cloudops/managed-secrets/auth0/LFX_V2_PCC (AUTH0 configuration)"
240+ echo " - /cloudops/managed-secrets/cloud/supabase/api_key (SUPABASE configuration)"
241+ echo ""
242+ echo "GitHub Secrets (required for authenticated tests):"
243+ echo " - TEST_USERNAME"
244+ echo " - TEST_PASSWORD"
245+
246+ - name : Generate test results summary
247+ id : test-results
248+ if : always()
249+ working-directory : apps/lfx-pcc
250+ run : |
251+ if [ "${{ steps.validate-secrets.outputs.can_run_tests }}" == "false" ]; then
252+ echo "⏭️ E2E tests skipped (missing required secrets)"
253+ echo "results=skipped" >> $GITHUB_OUTPUT
254+ elif [ -z "$TEST_USERNAME" ] || [ -z "$TEST_PASSWORD" ]; then
255+ echo "⏭️ E2E tests skipped (no test credentials)"
256+ echo "results=skipped" >> $GITHUB_OUTPUT
257+ elif [ -f "test-results/.last-run.json" ]; then
258+ echo "✅ E2E tests completed successfully"
259+ echo "results=success" >> $GITHUB_OUTPUT
260+ else
261+ echo "❌ E2E tests failed"
262+ echo "results=failure" >> $GITHUB_OUTPUT
263+ fi
264+
265+ - name : Upload Playwright report
266+ id : upload-report
267+ uses : actions/upload-artifact@v4
268+ if : always()
269+ with :
270+ name : playwright-report-${{ inputs.browser }}-${{ github.run_id }}
271+ path : |
272+ apps/lfx-pcc/playwright-report/
273+ retention-days : 7
274+
275+ - name : Comment test results on PR
276+ if : github.event_name == 'pull_request' && always()
277+ uses : actions/github-script@v7
278+ with :
279+ script : |
280+ const results = '${{ steps.test-results.outputs.results }}';
281+ const browser = '${{ inputs.browser }}';
282+ const runId = '${{ github.run_id }}';
283+
284+ let emoji, status, details;
285+
286+ if (results === 'success') {
287+ emoji = '✅';
288+ status = 'passed';
289+ details = 'All E2E tests passed successfully.';
290+ } else if (results === 'failure') {
291+ emoji = '❌';
292+ status = 'failed';
293+ details = 'Some E2E tests failed. Check the [test report](https://github.com/${{ github.repository }}/actions/runs/' + runId + ') for details.';
294+ } else {
295+ emoji = '⏭️';
296+ status = 'skipped';
297+ details = 'E2E tests were skipped (no test credentials provided).';
298+ }
299+
300+ const comment = `## ${emoji} E2E Tests ${status.charAt(0).toUpperCase() + status.slice(1)}
301+
302+ **Browser:** ${browser}
303+ **Status:** ${status}
304+
305+ ${details}
306+
307+ <details>
308+ <summary>Test Configuration</summary>
309+
310+ - **Node.js:** ${{ inputs.node-version }}
311+ - **Command:** \`${{ inputs.test-command }}\`
312+ - **Browser:** ${browser}
313+ - **Base URL:** ${{ inputs.base-url }}
314+
315+ </details>`;
316+
317+ // Look for existing E2E test comment by this bot
318+ const existingComments = await github.rest.issues.listComments({
319+ issue_number: context.issue.number,
320+ owner: context.repo.owner,
321+ repo: context.repo.repo
322+ });
323+
324+ const botComment = existingComments.data.find(comment =>
325+ comment.user.login === 'github-actions[bot]' &&
326+ comment.body.includes('## ') &&
327+ comment.body.includes('E2E Tests')
328+ );
329+
330+ if (botComment) {
331+ // Update existing comment
332+ await github.rest.issues.updateComment({
333+ comment_id: botComment.id,
334+ owner: context.repo.owner,
335+ repo: context.repo.repo,
336+ body: comment
337+ });
338+ console.log('Updated existing E2E test comment');
339+ } else {
340+ // Create new comment
341+ await github.rest.issues.createComment({
342+ issue_number: context.issue.number,
343+ owner: context.repo.owner,
344+ repo: context.repo.repo,
345+ body: comment
346+ });
347+ console.log('Created new E2E test comment');
348+ }
0 commit comments