Skip to content

Commit c7de015

Browse files
committed
orchestrator e2e tests
1 parent e2df0a3 commit c7de015

12 files changed

Lines changed: 1268 additions & 7 deletions

File tree

workspaces/orchestrator/.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
playwright.config.ts
2+
e2e-tests/
23
dist-dynamic
34
dist-scalprum
45
!.eslintrc.js

workspaces/orchestrator/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ site
5353
*.session.sql
5454

5555
# E2E test reports
56-
e2e-test-report/
56+
e2e-test-report*/
57+
test-results/
5758

5859
# Sonataflow Dev Container Temp files
5960
packages/backend/.devModeTemp
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { test, expect, Page, type BrowserContext } from '@playwright/test';
18+
import { Orchestrator } from './pages/orchestrator';
19+
import { runAccessibilityTests } from './utils/accessibility';
20+
import { OrchestratorHelper } from './utils/helper';
21+
import { OrchestratorMessages, getTranslations } from './utils/translations';
22+
23+
const LOCALE_DISPLAY_NAMES: Record<string, string> = {
24+
en: 'English',
25+
de: 'Deutsch',
26+
es: 'Español',
27+
fr: 'Français',
28+
it: 'Italiano',
29+
ja: '日本語',
30+
};
31+
32+
function escapeRegExp(value: string): string {
33+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
34+
}
35+
36+
function countHeadingPattern(template: string): RegExp {
37+
const regexBody = template.split('{{count}}').map(escapeRegExp).join('\\d+');
38+
return new RegExp(`^${regexBody}$`);
39+
}
40+
41+
/**
42+
* Get the display name for a locale code
43+
*/
44+
function getLocaleDisplayName(locale: string): string {
45+
const baseLocale = locale.split('-')[0];
46+
return LOCALE_DISPLAY_NAMES[baseLocale] || locale;
47+
}
48+
49+
test.describe('Orchestrator workflow runs', () => {
50+
let orchestrator: Orchestrator;
51+
let orchestratorHelper: OrchestratorHelper;
52+
let translations: OrchestratorMessages;
53+
let sharedPage!: Page;
54+
let sharedContext!: BrowserContext;
55+
56+
async function switchToLocale(page: Page, locale: string): Promise<void> {
57+
const baseLocale = locale.split('-')[0];
58+
if (baseLocale === 'en') return;
59+
60+
const displayName = getLocaleDisplayName(locale);
61+
const localeDisplayPattern = new RegExp(
62+
`^(${Object.values(LOCALE_DISPLAY_NAMES).map(escapeRegExp).join('|')})$`,
63+
);
64+
65+
await page.goto('/settings');
66+
await page.waitForLoadState('networkidle');
67+
const languageButton = page
68+
.getByRole('button', { name: localeDisplayPattern })
69+
.first();
70+
await languageButton.waitFor({ state: 'visible', timeout: 30_000 });
71+
await languageButton.click();
72+
await page.getByRole('option', { name: displayName }).click();
73+
await page.goto('/');
74+
}
75+
76+
test.beforeAll(async ({ browser }) => {
77+
sharedContext = await browser.newContext();
78+
sharedPage = await sharedContext.newPage();
79+
const currentLocale = await sharedPage.evaluate(
80+
() => globalThis.navigator.language.split('-')[0],
81+
);
82+
await sharedPage.goto('/');
83+
await sharedPage.getByRole('button', { name: 'Enter' }).click();
84+
await switchToLocale(sharedPage, currentLocale);
85+
translations = getTranslations(currentLocale);
86+
orchestrator = new Orchestrator(sharedPage, translations, currentLocale);
87+
orchestratorHelper = new OrchestratorHelper(sharedPage, translations);
88+
});
89+
90+
test.beforeEach(async () => {
91+
await orchestrator.navigateToOrchestrator();
92+
});
93+
94+
test.afterAll(async () => {
95+
if (sharedContext) {
96+
await sharedContext.close();
97+
}
98+
});
99+
100+
test.describe('Orchestrator > Workflow runs page', () => {
101+
test.beforeEach(async () => {
102+
await orchestrator.navigateToWorkflowRunTab(
103+
translations.page.tabs.workflows,
104+
);
105+
});
106+
107+
test('Verify workflow runs table', async ({
108+
browser: _browser,
109+
}, testInfo) => {
110+
await runAccessibilityTests(sharedPage, testInfo);
111+
await orchestratorHelper.verifyTableHeadingAndRows([
112+
translations.table.headers.name,
113+
translations.table.headers.workflowStatus,
114+
translations.table.headers.version,
115+
translations.table.headers.runsLastMonth,
116+
translations.table.headers.successRatio,
117+
'Actions',
118+
]);
119+
120+
await orchestratorHelper.searchInputPlaceholder('Hello World workflow');
121+
await expect(
122+
sharedPage
123+
.getByRole('row', { name: 'Hello World workflow' })
124+
.getByRole('button', {
125+
name: translations.table.actions.run,
126+
exact: true,
127+
})
128+
.first(),
129+
).toBeVisible();
130+
await expect(
131+
sharedPage.getByRole('row', { name: 'Hello World workflow' }),
132+
).toContainText(translations.workflow.status.available);
133+
await expect(
134+
sharedPage.getByRole('row', { name: 'Hello World workflow' }),
135+
).toContainText('1.0');
136+
});
137+
138+
test('Run Test Object Type Support in ui:props workflow', async ({
139+
browser: _browser,
140+
}) => {
141+
const workflowName = 'Test Object Type Support in ui:props';
142+
const workflowInputs = {
143+
name: 'test-name',
144+
email: 'test@test.com',
145+
simpleText: 'sample testing',
146+
objectExample: '{"kind":"demo","id":42,"tags":["a","b"]}',
147+
};
148+
149+
await orchestrator.runUiPropsWorkflow(workflowName, workflowInputs);
150+
151+
await expect(sharedPage).toHaveURL(/\/orchestrator\/instances\/.+/);
152+
await orchestratorHelper.verifyBreadcrumbLink(workflowName);
153+
await orchestrator.verifyUiPropsWorkflowInstanceDetails(workflowName);
154+
await orchestrator.verifyUiPropsWorkflowRunVariables(workflowInputs);
155+
});
156+
157+
test('Greeting workflow execution and workflow tab validation', async ({
158+
browser: _browser,
159+
}) => {
160+
const workflowName = 'Greeting workflow';
161+
162+
await orchestrator.runGreetingWorkflow(workflowName);
163+
await orchestrator.navigateToOrchestrator();
164+
await orchestrator.navigateToWorkflowRunTab(
165+
translations.page.tabs.workflows,
166+
);
167+
await orchestrator.searchWorkflow(workflowName);
168+
await orchestrator.validateGreetingWorkflowTableRow(workflowName);
169+
await sharedPage
170+
.getByRole('row', { name: 'Greeting workflow' })
171+
.getByRole('link', { name: workflowName })
172+
.first()
173+
.click();
174+
await orchestrator.validateWorkflowDetails(workflowName);
175+
});
176+
177+
test('Greeting workflow re-run and run details validation', async ({
178+
browser: _browser,
179+
}) => {
180+
const workflowName = 'Greeting workflow';
181+
182+
await orchestrator.runGreetingWorkflow(workflowName);
183+
await orchestrator.reRunGreetingWorkflow();
184+
await orchestrator.verifyWorkflowRunDetails();
185+
});
186+
187+
test('Sample Retry Test', async ({ browser: _browser }) => {
188+
const workflowName = 'Sample Retry Test';
189+
190+
await orchestrator.runSampleRetryTest(workflowName);
191+
await orchestrator.verifySampleRetryTest();
192+
});
193+
194+
test('Add workflow run by entity', async ({ browser: _browser }) => {
195+
await orchestrator.navigateToCatalog();
196+
await expect(
197+
sharedPage
198+
.getByRole('row', { name: 'my-component' })
199+
.getByRole('link', {
200+
name: 'user:guest',
201+
})
202+
.first(),
203+
).toBeVisible();
204+
await sharedPage.getByRole('link', { name: 'my-component' }).click();
205+
await expect(sharedPage.getByText('my-component')).toBeVisible();
206+
await sharedPage.getByRole('tab', { name: 'Workflows' }).first().click();
207+
await expect(
208+
sharedPage
209+
.getByRole('row', { name: 'Hello World Workflow' })
210+
.locator('button'),
211+
).toBeVisible();
212+
await sharedPage
213+
.getByRole('row', { name: 'Hello World Workflow' })
214+
.locator('button')
215+
.first()
216+
.click();
217+
await orchestratorHelper.clickButton(translations.workflow.buttons.run);
218+
await orchestratorHelper.verifyBreadcrumbLink('Hello World Workflow');
219+
});
220+
});
221+
222+
test.describe('Orchestrator > All runs page', () => {
223+
test.beforeEach(async () => {
224+
await orchestrator.navigateToWorkflowRunTab(
225+
translations.page.tabs.allRuns,
226+
);
227+
});
228+
229+
test('Verify all runs tab', async ({ browser: _browser }, testInfo) => {
230+
await orchestrator.navigateToWorkflowRunTab(
231+
translations.page.tabs.workflows,
232+
);
233+
await orchestrator.searchWorkflow('Hello World workflow');
234+
await sharedPage
235+
.getByRole('row', { name: 'Hello World workflow' })
236+
.getByRole('button', {
237+
name: translations.table.actions.run,
238+
exact: true,
239+
})
240+
.click();
241+
await orchestrator.submitWorkflowRunFromReview();
242+
await orchestratorHelper.verifyBreadcrumbLink('Hello World workflow');
243+
await orchestrator.navigateToOrchestrator();
244+
await orchestrator.navigateToWorkflowRunTab(
245+
translations.page.tabs.allRuns,
246+
);
247+
await expect(
248+
sharedPage.getByText(
249+
countHeadingPattern(translations.table.title.allRuns),
250+
),
251+
).toBeVisible();
252+
await runAccessibilityTests(sharedPage, testInfo);
253+
await orchestrator.verifyWorkflowRunTabDetails();
254+
});
255+
256+
// Remove the fixme with the fix of bug https://redhat.atlassian.net/browse/RHDHBUGS-3401
257+
test.fixme('All runs tab workflow details validation', async ({
258+
browser: _browser,
259+
}) => {
260+
await sharedPage
261+
.getByRole('link', { name: 'Hello World workflow' })
262+
.first()
263+
.click();
264+
await orchestratorHelper.verifyHeading('Hello World workflow');
265+
await orchestrator.verifyWorkflowDetails();
266+
await orchestrator.navigateToWorkflowRunTab(
267+
translations.page.tabs.workflowRuns,
268+
);
269+
await orchestrator.verifyWorkflowRunTab();
270+
const runLocator = await sharedPage
271+
.getByText(
272+
countHeadingPattern(translations.table.title.allWorkflowRuns),
273+
)
274+
.textContent();
275+
const runCount = parseInt(runLocator?.match(/\d+/)?.[0] || '0');
276+
await sharedPage
277+
.getByRole('button', {
278+
name: translations.table.actions.run,
279+
exact: true,
280+
})
281+
.first()
282+
.click();
283+
await orchestrator.submitWorkflowRunFromReview();
284+
await orchestratorHelper.verifyHeading(
285+
translations.run.pageTitle.replace(
286+
'{{processName}}',
287+
'Hello World workflow',
288+
),
289+
);
290+
await sharedPage.goto(`/orchestrator/workflows/hello_world/runs`);
291+
await orchestrator.verifyWorkflowRunTab(runCount + 1);
292+
});
293+
});
294+
});

0 commit comments

Comments
 (0)