Skip to content

Commit defa98a

Browse files
HusneShabbirHusneShabbir
andauthored
feat(bulk-import): Add e2e tests and playwright configuration (#1975)
- Add playwright.config.ts with serial test execution - Add e2e test utilities (apiUtils, ariaSnapshots, helpers, translations) - Add comprehensive e2e tests for bulk import functionality - Update app-config.yaml and package.json for e2e testing Co-authored-by: HusneShabbir <husneshabbir447@gmail.com>
1 parent b88e8dd commit defa98a

10 files changed

Lines changed: 907 additions & 13 deletions

File tree

workspaces/bulk-import/app-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ techdocs:
7272
auth:
7373
# see https://backstage.io/docs/auth/ to learn about auth providers
7474
providers:
75+
guest: {}
7576
# See https://backstage.io/docs/auth/guest/provider
7677
github:
7778
development:

workspaces/bulk-import/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@
3636
"directory": "workspaces/bulk-import"
3737
},
3838
"devDependencies": {
39+
"@axe-core/playwright": "^4.11.0",
3940
"@backstage/cli": "^0.34.5",
4041
"@backstage/e2e-test-utils": "^0.1.1",
4142
"@backstage/repo-tools": "^0.16.0",
4243
"@changesets/cli": "^2.27.1",
4344
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
45+
"@playwright/test": "1.56.1",
4446
"@spotify/prettier-config": "^12.0.0",
4547
"knip": "^5.27.4",
4648
"node-gyp": "^9.0.0",

workspaces/bulk-import/packages/app/e2e-tests/app.test.ts

Lines changed: 231 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,238 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { expect, test } from '@playwright/test';
17+
import { BrowserContext, Page, expect, test, TestInfo } from '@playwright/test';
1818

19-
test('App should render the welcome page', async ({ page }) => {
20-
await page.goto('/');
19+
import {
20+
mockBulkImportByRepoFrontendResponse,
21+
mockBulkImportByRepoResponse,
22+
mockBulkImportDryRunResponse,
23+
mockBulkImportImportsResponse,
24+
mockBulkImportRepositoriesResponse,
25+
mockImportByRepoData,
26+
mockImportByRepoFrontendData,
27+
mockImportsData,
28+
mockImportsDryRunData,
29+
mockRepositoriesData,
30+
} from './utils/apiUtils';
31+
import {
32+
getPreviewSidebarSnapshots,
33+
PreviewSidebarSnapshotsType,
34+
} from './utils/ariaSnapshots';
35+
import { runAccessibilityTests, switchToLocale } from './utils/helpers';
36+
import {
37+
BulkImportMessages,
38+
getSelectedRepositoriesHeading,
39+
getTranslations,
40+
} from './utils/translations';
2141

22-
const enterButton = page.getByRole('button', { name: 'Enter' });
23-
await expect(enterButton).toBeVisible();
24-
await enterButton.click();
42+
test.describe('Bulk Import', () => {
43+
test.describe.configure({ mode: 'serial' });
2544

26-
await expect(page.getByText('My Company Catalog')).toBeVisible();
45+
let sharedPage: Page;
46+
let translations: BulkImportMessages;
47+
let previewSidebarSnapshots: PreviewSidebarSnapshotsType;
48+
let context: BrowserContext;
49+
50+
test.beforeAll(async ({ browser }) => {
51+
context = await browser.newContext();
52+
sharedPage = await context.newPage();
53+
await mockBulkImportRepositoriesResponse(sharedPage, mockRepositoriesData);
54+
await sharedPage.goto('/');
55+
56+
const enterButton = sharedPage.getByRole('button', { name: 'Enter' });
57+
await expect(enterButton).toBeVisible();
58+
await enterButton.click();
59+
60+
const currentLocale = await sharedPage.evaluate(
61+
() => globalThis.navigator.language,
62+
);
63+
64+
await switchToLocale(sharedPage, currentLocale);
65+
translations = getTranslations(currentLocale);
66+
previewSidebarSnapshots = getPreviewSidebarSnapshots(translations);
67+
68+
await expect(sharedPage.getByText('My Company Catalog')).toBeVisible();
69+
70+
await expect(
71+
sharedPage.getByRole('link', { name: translations.sidebar.bulkImport }),
72+
).toBeVisible();
73+
await sharedPage
74+
.getByRole('link', { name: translations.sidebar.bulkImport })
75+
.click();
76+
});
77+
78+
test.afterAll(async () => {
79+
await context.close();
80+
});
81+
82+
test('should render bulk import page with source control options, search, and repositories table', async ({
83+
browser,
84+
}, testInfo: TestInfo) => {
85+
const article = sharedPage.getByRole('article');
86+
87+
await expect(article).toMatchAriaSnapshot(`
88+
- paragraph: ${translations.addRepositories.approvalTool.title}
89+
- paragraph
90+
- radiogroup:
91+
- radio "${translations.addRepositories.approvalTool.github}"
92+
- text: ${translations.addRepositories.approvalTool.github}
93+
- radio "${translations.addRepositories.approvalTool.gitlab}"
94+
- text: ${translations.addRepositories.approvalTool.gitlab}
95+
`);
96+
97+
const selectedReposHeading = getSelectedRepositoriesHeading(
98+
translations,
99+
0,
100+
);
101+
await expect(article).toMatchAriaSnapshot(`
102+
- heading "${selectedReposHeading}"
103+
- textbox "search"
104+
- button "${translations.addRepositories.clearSearch}"
105+
`);
106+
107+
await expect(article).toMatchAriaSnapshot(`
108+
- table:
109+
- rowgroup:
110+
- row "select all repositories ${translations.table.headers.name} ${translations.table.headers.url} ${translations.table.headers.organization} ${translations.table.headers.status}":
111+
- columnheader "select all repositories ${translations.table.headers.name}":
112+
- checkbox "select all repositories"
113+
- button "${translations.table.headers.name}"
114+
- columnheader "${translations.table.headers.url}":
115+
- button "${translations.table.headers.url}"
116+
- columnheader "${translations.table.headers.organization}":
117+
- button "${translations.table.headers.organization}"
118+
- columnheader "${translations.table.headers.status}":
119+
- button "${translations.table.headers.status}"
120+
`);
121+
122+
await runAccessibilityTests(sharedPage, testInfo);
123+
});
124+
125+
test('Verify selected repositories count change', async () => {
126+
const selectAllCheckbox = sharedPage.getByRole('checkbox', {
127+
name: 'select all repositories',
128+
});
129+
const headingId = `${translations.addRepositories.selectedLabel} ${translations.addRepositories.selectedRepositories}`;
130+
const selectedReposHeading = sharedPage.locator(`[id="${headingId}"]`);
131+
132+
const zeroSelected = getSelectedRepositoriesHeading(translations, 0);
133+
const fiveSelected = getSelectedRepositoriesHeading(translations, 5);
134+
135+
await expect(selectedReposHeading).toContainText(zeroSelected);
136+
await selectAllCheckbox.check();
137+
await expect(selectAllCheckbox).toBeChecked();
138+
await expect(selectedReposHeading).toContainText(fiveSelected);
139+
const readyToImport = await sharedPage
140+
.getByText(translations.status.readyToImport)
141+
.count();
142+
expect(readyToImport).toEqual(5);
143+
await selectAllCheckbox.uncheck();
144+
await expect(selectAllCheckbox).not.toBeChecked();
145+
});
146+
147+
test('Verify preview sidebar', async ({ browser }, testInfo: TestInfo) => {
148+
const backendServiceCheckbox = sharedPage
149+
.getByRole('rowheader', { name: 'backend-service' })
150+
.getByRole('checkbox');
151+
const sidebar = sharedPage.getByTestId('preview-pullrequest-sidebar');
152+
153+
await backendServiceCheckbox.check();
154+
await expect(
155+
sharedPage.getByText(translations.status.readyToImport).first(),
156+
).toBeVisible();
157+
await sharedPage.getByTestId('preview-file').first().click();
158+
await expect(sidebar.locator('h5')).toContainText('backend-service');
159+
await expect(sidebar).toMatchAriaSnapshot(`
160+
- button "${translations.common.save}"
161+
- link "${translations.common.cancel}":
162+
- /url: /bulk-import/repositories
163+
- button "${translations.common.cancel}"
164+
`);
165+
166+
// Validate each section individually for better readability
167+
await expect(sidebar).toMatchAriaSnapshot(
168+
previewSidebarSnapshots.pullRequestDetails,
169+
);
170+
await expect(sidebar).toMatchAriaSnapshot(
171+
previewSidebarSnapshots.entityConfigHeader,
172+
);
173+
await expect(sidebar).toMatchAriaSnapshot(
174+
previewSidebarSnapshots.entityOwner,
175+
);
176+
await expect(sidebar).toMatchAriaSnapshot(
177+
previewSidebarSnapshots.codeownersOption,
178+
);
179+
await expect(sidebar).toMatchAriaSnapshot(
180+
previewSidebarSnapshots.annotations,
181+
);
182+
await expect(sidebar).toMatchAriaSnapshot(previewSidebarSnapshots.labels);
183+
await expect(sidebar).toMatchAriaSnapshot(previewSidebarSnapshots.spec);
184+
await expect(sidebar).toMatchAriaSnapshot(
185+
previewSidebarSnapshots.previewPullRequest,
186+
);
187+
await expect(sidebar).toMatchAriaSnapshot(
188+
previewSidebarSnapshots.previewEntities,
189+
);
190+
191+
await runAccessibilityTests(sharedPage, testInfo);
192+
193+
await sharedPage
194+
.getByRole('button', { name: translations.common.cancel })
195+
.click();
196+
});
197+
198+
test('Verify Import flow', async () => {
199+
const backendServiceCheckbox = sharedPage
200+
.getByRole('rowheader', { name: 'backend-service' })
201+
.getByRole('checkbox');
202+
const frontendAppCheckbox = sharedPage
203+
.getByRole('rowheader', { name: 'frontend-app' })
204+
.getByRole('checkbox');
205+
const addRepoFooter = sharedPage.getByTestId('add-repository-footer');
206+
207+
await mockBulkImportDryRunResponse(sharedPage, mockImportsDryRunData);
208+
await mockBulkImportImportsResponse(sharedPage, mockImportsData);
209+
await mockBulkImportByRepoResponse(sharedPage, mockImportByRepoData);
210+
await mockBulkImportByRepoFrontendResponse(
211+
sharedPage,
212+
mockImportByRepoFrontendData,
213+
);
214+
await backendServiceCheckbox.check();
215+
await frontendAppCheckbox.check();
216+
await expect(addRepoFooter).toMatchAriaSnapshot(`
217+
- button "${translations.common.import}"
218+
- link "${translations.common.cancel}":
219+
- /url: /bulk-import/repositories
220+
- button "${translations.common.cancel}"
221+
`);
222+
await sharedPage
223+
.getByTestId('add-repository-footer')
224+
.getByRole('button', { name: translations.common.import })
225+
.click();
226+
await sharedPage.waitForTimeout(2000);
227+
await expect(
228+
sharedPage.getByText(translations.status.imported),
229+
).toBeVisible();
230+
await expect(sharedPage.locator('tbody')).toMatchAriaSnapshot(`
231+
- cell "${translations.status.waitingForApproval} ${translations.repositories.pr} , Opens in a new window":
232+
- link "${translations.repositories.pr} , Opens in a new window":
233+
- /url: https://github.com/test-org/frontend-app/pull/1
234+
`);
235+
});
236+
237+
test('test rows dropdown', async () => {
238+
await sharedPage
239+
.getByRole('combobox', { name: translations.table.pagination.rows5 })
240+
.click();
241+
await expect(sharedPage.getByRole('listbox')).toMatchAriaSnapshot(`
242+
- listbox:
243+
- option "${translations.table.pagination.rows5}"
244+
- option "${translations.table.pagination.rows10}"
245+
- option "${translations.table.pagination.rows20}"
246+
- option "${translations.table.pagination.rows50}"
247+
- option "${translations.table.pagination.rows100}"
248+
`);
249+
await sharedPage.keyboard.press('Escape');
250+
});
27251
});

0 commit comments

Comments
 (0)