Skip to content

Commit 132844e

Browse files
authored
test: add e2e tests for public roadmap feature (#86)
Add comprehensive Playwright e2e tests for the roadmap board: - 14 API tests covering board, idea, and comments endpoints - 18 UI tests covering layout, columns, filtering, and detail panel - Add data-testid attributes to all roadmap component templates - Wire Atlassian credentials into e2e workflow via AWS Secrets Manager Signed-off-by: Asitha de Silva <[email protected]>
1 parent 96305f7 commit 132844e

File tree

9 files changed

+719
-23
lines changed

9 files changed

+719
-23
lines changed

.github/workflows/e2e.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jobs:
5454
secret-ids: |
5555
AUTH0, /cloudops/managed-secrets/auth0/LFX_Changelog
5656
AUTH0_COOKIE_SECRET, /cloudops/managed-secrets/cloud/changelog/auth0_secret
57+
ATLASSIAN, /cloudops/managed-secrets/cloud/changelog/atlassian_api
5758
5859
- name: Set up non-sensitive environment variables
5960
run: |
@@ -67,7 +68,6 @@ jobs:
6768
echo "OPENSEARCH_URL=http://localhost:9202" >> "$GITHUB_ENV"
6869
echo "SKIP_RATE_LIMIT=true" >> "$GITHUB_ENV"
6970
echo "CI=true" >> "$GITHUB_ENV"
70-
7171
# Slack OAuth (random values for e2e — tests verify auth/validation, not real Slack calls)
7272
echo "SLACK_CLIENT_ID=$(openssl rand -hex 16)" >> "$GITHUB_ENV"
7373
echo "SLACK_CLIENT_SECRET=$(openssl rand -hex 24)" >> "$GITHUB_ENV"
@@ -94,6 +94,7 @@ jobs:
9494
E2E_PRODUCT_ADMIN_EMAIL: ${{ secrets.E2E_PRODUCT_ADMIN_EMAIL }}
9595
E2E_EDITOR_EMAIL: ${{ secrets.E2E_EDITOR_EMAIL }}
9696
E2E_USER_EMAIL: ${{ secrets.E2E_USER_EMAIL }}
97+
ATLASSIAN_CLOUD_ID: ${{ secrets.ATLASSIAN_CLOUD_ID }}
9798
run: |
9899
# Parse Auth0 app credentials from JSON secret
99100
AUTH0_CLIENT_ID=$(echo "$AUTH0" | jq -r '.client_id')
@@ -151,6 +152,28 @@ jobs:
151152
echo "E2E_EDITOR_EMAIL=$E2E_EDITOR_EMAIL" >> "$GITHUB_ENV"
152153
echo "E2E_USER_EMAIL=$E2E_USER_EMAIL" >> "$GITHUB_ENV"
153154
155+
# Parse Atlassian / Jira credentials from AWS secret (required for roadmap tests)
156+
ATLASSIAN_EMAIL_VALUE=$(echo "$ATLASSIAN" | jq -r '.atlassian_email')
157+
ATLASSIAN_API_KEY_VALUE=$(echo "$ATLASSIAN" | jq -r '.atlassian_api_key')
158+
159+
if [ -z "$ATLASSIAN_EMAIL_VALUE" ] || [ "$ATLASSIAN_EMAIL_VALUE" = "null" ]; then
160+
echo "::error::Failed to parse atlassian_email from AWS secret"
161+
exit 1
162+
fi
163+
if [ -z "$ATLASSIAN_API_KEY_VALUE" ] || [ "$ATLASSIAN_API_KEY_VALUE" = "null" ]; then
164+
echo "::error::Failed to parse atlassian_api_key from AWS secret"
165+
exit 1
166+
fi
167+
168+
echo "::add-mask::$ATLASSIAN_EMAIL_VALUE"
169+
echo "::add-mask::$ATLASSIAN_API_KEY_VALUE"
170+
171+
echo "ATLASSIAN_EMAIL=$ATLASSIAN_EMAIL_VALUE" >> "$GITHUB_ENV"
172+
echo "ATLASSIAN_API_KEY=$ATLASSIAN_API_KEY_VALUE" >> "$GITHUB_ENV"
173+
174+
echo "::add-mask::$ATLASSIAN_CLOUD_ID"
175+
echo "ATLASSIAN_CLOUD_ID=$ATLASSIAN_CLOUD_ID" >> "$GITHUB_ENV"
176+
154177
- name: Install Playwright Chromium
155178
run: npx playwright install chromium --with-deps
156179

apps/lfx-changelog/.env.e2e.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ OPENSEARCH_URL=http://localhost:9202
1414
# Skip rate limiting during E2E tests
1515
SKIP_RATE_LIMIT=true
1616

17+
# Atlassian / Jira (required for roadmap tests)
18+
ATLASSIAN_EMAIL=
19+
ATLASSIAN_API_KEY=
20+
ATLASSIAN_CLOUD_ID=
21+
1722
# Slack OAuth (required for Slack integration tests)
1823
SLACK_CLIENT_ID=
1924
SLACK_CLIENT_SECRET=
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright The Linux Foundation and each contributor to LFX.
2+
// SPDX-License-Identifier: MIT
3+
4+
import type { Locator, Page } from '@playwright/test';
5+
6+
export class RoadmapBoardPage {
7+
public readonly board: Locator;
8+
public readonly heading: Locator;
9+
public readonly totalIdeas: Locator;
10+
public readonly syncedLabel: Locator;
11+
public readonly teamFilters: Locator;
12+
public readonly teamClear: Locator;
13+
public readonly loading: Locator;
14+
public readonly kanban: Locator;
15+
public readonly toggleCompleted: Locator;
16+
public readonly detailBackdrop: Locator;
17+
public readonly detailPanel: Locator;
18+
public readonly detailClose: Locator;
19+
public readonly detailLoading: Locator;
20+
public readonly detailSummary: Locator;
21+
public readonly detailColumn: Locator;
22+
public readonly detailKey: Locator;
23+
public readonly detailDescription: Locator;
24+
public readonly detailComments: Locator;
25+
public readonly detailNotFound: Locator;
26+
27+
public constructor(public readonly page: Page) {
28+
this.board = page.locator('[data-testid="roadmap-board"]');
29+
this.heading = page.locator('[data-testid="roadmap-heading"]');
30+
this.totalIdeas = page.locator('[data-testid="roadmap-total-ideas"]');
31+
this.syncedLabel = page.locator('[data-testid="roadmap-synced-label"]');
32+
this.teamFilters = page.locator('[data-testid="roadmap-team-filters"]');
33+
this.teamClear = page.locator('[data-testid="roadmap-team-clear"]');
34+
this.loading = page.locator('[data-testid="roadmap-loading"]');
35+
this.kanban = page.locator('[data-testid="roadmap-kanban"]');
36+
this.toggleCompleted = page.locator('[data-testid="roadmap-toggle-completed"]');
37+
this.detailBackdrop = page.locator('[data-testid="roadmap-detail-backdrop"]');
38+
this.detailPanel = page.locator('[data-testid="roadmap-detail-panel"]');
39+
this.detailClose = page.locator('[data-testid="roadmap-detail-close"]');
40+
this.detailLoading = page.locator('[data-testid="roadmap-detail-loading"]');
41+
this.detailSummary = page.locator('[data-testid="roadmap-detail-summary"]');
42+
this.detailColumn = page.locator('[data-testid="roadmap-detail-column"]');
43+
this.detailKey = page.locator('[data-testid="roadmap-detail-key"]');
44+
this.detailDescription = page.locator('[data-testid="roadmap-detail-description"]');
45+
this.detailComments = page.locator('[data-testid="roadmap-detail-comments"]');
46+
this.detailNotFound = page.locator('[data-testid="roadmap-detail-not-found"]');
47+
}
48+
49+
public async goto() {
50+
await this.page.goto('/roadmap', { waitUntil: 'networkidle' });
51+
}
52+
53+
public getColumn(name: string): Locator {
54+
return this.page.locator(`[data-testid="roadmap-column-${name}"]`);
55+
}
56+
57+
public getColumnCount(name: string): Locator {
58+
return this.getColumn(name).locator('[data-testid="roadmap-column-count"]');
59+
}
60+
61+
public getColumnEmpty(name: string): Locator {
62+
return this.getColumn(name).locator('[data-testid="roadmap-column-empty"]');
63+
}
64+
65+
public getTeamPill(team: string): Locator {
66+
return this.page.locator(`[data-testid="roadmap-team-pill-${team}"]`);
67+
}
68+
69+
public getCard(jiraKey: string): Locator {
70+
return this.page.locator(`[data-testid="roadmap-card-${jiraKey}"]`);
71+
}
72+
73+
public getCards(): Locator {
74+
return this.page.locator('[data-testid^="roadmap-card-"]');
75+
}
76+
77+
public getColumns(): Locator {
78+
return this.page.locator('[data-testid^="roadmap-column-"]').filter({ hasNot: this.page.locator('[data-testid^="roadmap-column-"]') });
79+
}
80+
}

0 commit comments

Comments
 (0)