Skip to content

Commit 8e66186

Browse files
committed
orchestrator e2e tests
1 parent 0496a27 commit 8e66186

13 files changed

Lines changed: 1174 additions & 19 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: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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('Orchestrator');
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.lastRun,
115+
translations.table.headers.lastRunStatus,
116+
translations.table.headers.description,
117+
translations.table.headers.version,
118+
'Actions',
119+
]);
120+
121+
await orchestratorHelper.searchInputPlaceholder('Hello World workflow');
122+
await expect(
123+
sharedPage
124+
.getByRole('row', { name: 'Hello World workflow' })
125+
.getByRole('button', {
126+
name: translations.table.actions.run,
127+
exact: true,
128+
})
129+
.first(),
130+
).toBeVisible();
131+
await expect(
132+
sharedPage.getByRole('row', { name: 'Hello World workflow' }),
133+
).toContainText(translations.workflow.status.available);
134+
await sharedPage
135+
.getByRole('row', { name: 'Hello World workflow' })
136+
.getByRole('button', {
137+
name: translations.table.actions.viewInputSchema,
138+
})
139+
.click();
140+
const workflowSchema = sharedPage.getByRole('dialog', {
141+
name: /Hello World Workflow input schema/i,
142+
});
143+
await expect(workflowSchema).toBeVisible();
144+
await expect(
145+
workflowSchema.getByText(translations.messages.noInputSchemaWorkflow),
146+
).toBeVisible();
147+
await sharedPage
148+
.getByRole('button', { name: 'close', exact: true })
149+
.click();
150+
});
151+
152+
test('Run Test Object Type Support in ui:props workflow', async ({
153+
browser: _browser,
154+
}) => {
155+
const workflowName = 'Test Object Type Support in ui:props';
156+
const workflowInputs = {
157+
name: 'test-name',
158+
email: 'test@test.com',
159+
simpleText: 'sample testing',
160+
objectExample: '{"kind":"demo","id":42,"tags":["a","b"]}',
161+
};
162+
163+
await orchestrator.runUiPropsWorkflow(workflowName, workflowInputs);
164+
165+
await expect(sharedPage).toHaveURL(/\/orchestrator\/instances\/.+/);
166+
await orchestratorHelper.verifyHeading(
167+
translations.run.pageTitle.replace('{{processName}}', workflowName),
168+
);
169+
await orchestrator.verifyUiPropsWorkflowInstanceDetails(workflowName);
170+
await orchestrator.verifyUiPropsWorkflowRunVariables(workflowInputs);
171+
});
172+
173+
test('Greeting workflow execution and workflow tab validation', async ({
174+
browser: _browser,
175+
}) => {
176+
const workflowName = 'Greeting workflow';
177+
178+
await orchestrator.runGreetingWorkflow(workflowName);
179+
await orchestrator.navigateToOrchestrator('Orchestrator');
180+
await orchestrator.navigateToWorkflowRunTab(
181+
translations.page.tabs.workflows,
182+
);
183+
await orchestrator.searchWorkflow(workflowName);
184+
await orchestrator.validateGreetingWorkflowTableRow(workflowName);
185+
});
186+
187+
test('Greeting workflow run details validation', async ({
188+
browser: _browser,
189+
}) => {
190+
const workflowName = 'Greeting workflow';
191+
192+
await orchestrator.runGreetingWorkflow(workflowName);
193+
await orchestrator.reRunGreetingWorkflow();
194+
await orchestrator.verifyWorkflowRunDetails();
195+
});
196+
197+
test('Sample Retry Test', async ({ browser: _browser }) => {
198+
const workflowName = 'Sample Retry Test';
199+
200+
await orchestrator.runSampleRetryTest(workflowName);
201+
await orchestrator.verifySampleRetryTest();
202+
});
203+
});
204+
205+
test.describe('Orchestrator > All runs page', () => {
206+
test.beforeEach(async () => {
207+
await orchestrator.navigateToWorkflowRunTab(
208+
translations.page.tabs.allRuns,
209+
);
210+
});
211+
212+
test('Verify all runs tab', async ({ browser: _browser }, testInfo) => {
213+
await orchestrator.navigateToWorkflowRunTab(
214+
translations.page.tabs.workflows,
215+
);
216+
await orchestrator.searchWorkflow('Hello World workflow');
217+
await sharedPage
218+
.getByRole('row', { name: 'Hello World workflow' })
219+
.getByRole('button', {
220+
name: translations.table.actions.run,
221+
exact: true,
222+
})
223+
.click();
224+
await orchestrator.submitWorkflowRunFromReview();
225+
await orchestratorHelper.verifyHeading(
226+
translations.run.pageTitle.replace(
227+
'{{processName}}',
228+
'Hello World workflow',
229+
),
230+
);
231+
await orchestrator.navigateToOrchestrator('Orchestrator');
232+
await orchestrator.navigateToWorkflowRunTab(
233+
translations.page.tabs.allRuns,
234+
);
235+
await expect(
236+
sharedPage.getByText(
237+
countHeadingPattern(translations.table.title.allRuns),
238+
),
239+
).toBeVisible();
240+
await runAccessibilityTests(sharedPage, testInfo);
241+
await orchestrator.verifyWorkflowRunTabDetails();
242+
});
243+
244+
// Remove the fixme with the fix of bug https://redhat.atlassian.net/browse/RHDHBUGS-3401
245+
test.fixme('All runs tab workflow details validation', async ({
246+
browser: _browser,
247+
}) => {
248+
await sharedPage
249+
.getByRole('link', { name: 'Hello World workflow' })
250+
.first()
251+
.click();
252+
await orchestratorHelper.verifyHeading('Hello World workflow');
253+
await orchestrator.verifyWorkflowDetails();
254+
await orchestrator.navigateToWorkflowRunTab(
255+
translations.page.tabs.workflowRuns,
256+
);
257+
await orchestrator.verifyWorkflowRunTab();
258+
const runLocator = await sharedPage
259+
.getByText(
260+
countHeadingPattern(translations.table.title.allWorkflowRuns),
261+
)
262+
.textContent();
263+
const runCount = parseInt(runLocator?.match(/\d+/)?.[0] || '0');
264+
await sharedPage
265+
.getByRole('button', {
266+
name: translations.table.actions.run,
267+
exact: true,
268+
})
269+
.first()
270+
.click();
271+
await orchestrator.submitWorkflowRunFromReview();
272+
await orchestratorHelper.verifyHeading(
273+
translations.run.pageTitle.replace(
274+
'{{processName}}',
275+
'Hello World workflow',
276+
),
277+
);
278+
await sharedPage.goto(`/orchestrator/workflows/hello_world/runs`);
279+
await orchestrator.verifyWorkflowRunTab(runCount + 1);
280+
});
281+
});
282+
});

0 commit comments

Comments
 (0)